论坛首页 Java企业应用论坛

JAVA判断输入流字符编码的困惑

浏览 4740 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-06-02  
近日在开发爬虫程序时发现,如果事先不指定正确的字符集编码,在得到InputStream字节流实例后使用程序自身去判断,相关代码如下:
if(charset == null || "".equals(charset)) {
	reader = new InputStreamReader(inputStream);
	charset = reader.getEncoding();
}else {
	reader = new InputStreamReader(inputStream, charset);
}		

在if块语句中,往往会得到错误的charset,原因是创建了一个使用系统平台字符集的 InputStreamReader实例,同时很多专业网站在制作时使用了一个小技巧,就是在文件开头敲空格等,这样就会造成JDK的相关类在判断抓取到的输入流是什么编码出现错误,继而抓取下来的都是包含乱码的网页。例如抓取http://www.chinahr.com首页代码,根据if中的程序判断,charset=”UTF8”,而页面实际设置了charset=”gb2312”。从这里也可看出JDK在底层的字节流,字符流的实现上仍然是不够成熟,容易出现错误。
因为在构成InputStreamReader实例时的字符集出错,所以即使对抓取到的乱码的网页字符串重新转码也得不到正确的结果。
由于inputStream字节流只允许读取一遍,往往还不支持mark(int),reset()等方法,所以根据这个特性,思考了若干解决方案,其中比较接近的一个是,先使用缺省字符集将字节流inputStream转换为字符流InputStreamReader,再使用BufferedReader类包装一层,在BufferedReader读取到包含charset的时候,对charset进行判断后,重新实例化InputStreamReader,然后接着逐行读取。代码如下:
//原始的BufferedReader实例,reader即为上面代码产生的实例
bufferedReader = new BufferedReader(reader);
boolean mark = false;
		
StringBuffer  buffer = new StringBuffer();
String str = "";
int count = 0;
while ((str = bufferedReader.readLine()) != null) {
	if(mark && count > 0) {
		bufferedReader.reset();
		count = 0;
	}				
			
	buffer.append(str).append("\n");
			
	if(!mark){
		count ++;
		String tempStr = str.toLowerCase();
		if(tempStr.indexOf(DetectorConstants.HtmlTagProperty.HTTP_EQUIV) != -1 
		&& tempStr.indexOf(DetectorConstants.HtmlTagProperty.CHARSET) != -1){
			//此处略过了实际分析过程,直接给出结果
			String anotherCharset = "gb2312";
			if(anotherCharset != null && !"".equals(anotherCharset) && !anotherCharset.equals(charset)){
				charset = anotherCharset;						
				reader = new InputStreamReader(urlStream, anotherCharset);
				bufferedReader = new BufferedReader(reader);
						
				int av = urlStream.available();
				bufferedReader.mark(av + 1);//也可以使用count试试
				mark = true;
			}
		}				
	}
}

这种方法可以得到正确的编码格式的page页面,然而由于使用不同的字符集实例化InputStreamReader,造成inputStream流在使用新的字符集重新实例化后,之前的定位发生变化,前后的位置不一致,中间往往会漏掉大约400多行字符。
研究了一些相关开源项目,例如HtmlParser,发现也是无法提供一个正确的,好用的方法来判断字节流的编码格式。
   发表时间:2010-06-02  
sharong 写道
近日在开发爬虫程序时发现,如果事先不指定正确的字符集编码,在得到InputStream字节流实例后使用程序自身去判断,相关代码如下:
if(charset == null || "".equals(charset)) {
	reader = new InputStreamReader(inputStream);
	charset = reader.getEncoding();
}else {
	reader = new InputStreamReader(inputStream, charset);
}		

在if块语句中,往往会得到错误的charset,原因是创建了一个使用系统平台字符集的 InputStreamReader实例,同时很多专业网站在制作时使用了一个小技巧,就是在文件开头敲空格等,这样就会造成JDK的相关类在判断抓取到的输入流是什么编码出现错误,继而抓取下来的都是包含乱码的网页。例如抓取http://www.chinahr.com首页代码,根据if中的程序判断,charset=”UTF8”,而页面实际设置了charset=”gb2312”。从这里也可看出JDK在底层的字节流,字符流的实现上仍然是不够成熟,容易出现错误。
因为在构成InputStreamReader实例时的字符集出错,所以即使对抓取到的乱码的网页字符串重新转码也得不到正确的结果。
由于inputStream字节流只允许读取一遍,往往还不支持mark(int),reset()等方法,所以根据这个特性,思考了若干解决方案,其中比较接近的一个是,先使用缺省字符集将字节流inputStream转换为字符流InputStreamReader,再使用BufferedReader类包装一层,在BufferedReader读取到包含charset的时候,对charset进行判断后,重新实例化InputStreamReader,然后接着逐行读取。代码如下:
//原始的BufferedReader实例,reader即为上面代码产生的实例
bufferedReader = new BufferedReader(reader);
boolean mark = false;
		
StringBuffer  buffer = new StringBuffer();
String str = "";
int count = 0;
while ((str = bufferedReader.readLine()) != null) {
	if(mark && count > 0) {
		bufferedReader.reset();
		count = 0;
	}				
			
	buffer.append(str).append("\n");
			
	if(!mark){
		count ++;
		String tempStr = str.toLowerCase();
		if(tempStr.indexOf(DetectorConstants.HtmlTagProperty.HTTP_EQUIV) != -1 
		&& tempStr.indexOf(DetectorConstants.HtmlTagProperty.CHARSET) != -1){
			//此处略过了实际分析过程,直接给出结果
			String anotherCharset = "gb2312";
			if(anotherCharset != null && !"".equals(anotherCharset) && !anotherCharset.equals(charset)){
				charset = anotherCharset;						
				reader = new InputStreamReader(urlStream, anotherCharset);
				bufferedReader = new BufferedReader(reader);
						
				int av = urlStream.available();
				bufferedReader.mark(av + 1);//也可以使用count试试
				mark = true;
			}
		}				
	}
}

这种方法可以得到正确的编码格式的page页面,然而由于使用不同的字符集实例化InputStreamReader,造成inputStream流在使用新的字符集重新实例化后,之前的定位发生变化,前后的位置不一致,中间往往会漏掉大约400多行字符。
研究了一些相关开源项目,例如HtmlParser,发现也是无法提供一个正确的,好用的方法来判断字节流的编码格式。


1. 难道不能够先把inputStream下载下来,然后再自己去判断字符,再去转化么?不明白为什么要下载2次

2. available 是一个估计值,你可以用获得的byte[]长度作为av
0 请登录后投票
   发表时间:2010-07-04  
我的方法不就是边下载字节流,边转换为字符流进行读取。请问怎样全部下载下来,然后再去判断?难道不需要转化为字符流么?
0 请登录后投票
   发表时间:2010-07-05  
难道不能直接获取http响应编码吗?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics