`
sharong
  • 浏览: 493438 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
D1667ae2-8cfc-3b68-ac7c-5e282789fa4a
论开源
浏览量:8720
7eb53364-fe48-371c-9623-887640be0185
Spring-data-j...
浏览量:13059
社区版块
存档分类
最新评论

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

阅读更多
近日在开发爬虫程序时发现,如果事先不指定正确的字符集编码,在得到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,发现也是无法提供一个正确的,好用的方法来判断字节流的编码格式。
分享到:
评论
3 楼 rain2005 2010-07-05  
难道不能直接获取http响应编码吗?
2 楼 sharong 2010-07-04  
我的方法不就是边下载字节流,边转换为字符流进行读取。请问怎样全部下载下来,然后再去判断?难道不需要转化为字符流么?
1 楼 beneo 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

相关推荐

    java输入流、解决字符编码

    运用io流完美解决:字符编码问题。

    java 输入输出流

    字符流通常比字节流更适合处理文本数据,因为它们能自动处理字符编码。 3. **缓冲流**:为了提高性能,Java提供了缓冲流,如`BufferedReader`和`BufferedWriter`,它们在底层字节或字符流之上添加了一个缓冲区,...

    JAVA及相关字符集编码问题

    JAVA及相关字符集编码问题 在深入探讨JAVA与字符集编码问题之前,我们首先需要理解不同字符集编码的基本概念以及它们在JAVA环境中的应用。字符集编码是计算机系统中表示文字的一种方式,它决定了如何将字符转换为二...

    JAVA中文字符编码问题详解.doc

    1. 按指定的字符编码形式,从源输入流中读取字符数据 2. 以 UNICODE 编码形式将字符数据存储在内存中 3. 按指定的字符编码形式,将字符数据编码并写入目的输出流中 因此,JAVA 处理字符时总是经过了两次编码转换,...

    java 判断汉字 汉字的判断

    ### Java判断字符串是否包含汉字的方法 在开发过程中,有时我们需要判断一个字符串是否包含汉字,并根据判断的结果执行不同的逻辑。这通常出现在需要处理多语言输入的情况下,例如用户输入验证、文本分析等场景。...

    字符串长度的判断 JAVA

    在Java编程语言中,字符...理解字符串的长度判断和截取是Java编程的基础,它们在实际编程中有着广泛的应用,例如在输入验证、数据处理、格式化输出等场景。熟练掌握这些基本操作,将有助于编写出更加高效和可靠的代码。

    JAVA 转换字符编码工具

    - `InputStreamReader` 和 `OutputStreamWriter`:它们可以与`InputStream`和`OutputStream`结合使用,用于在字节流和字符流之间进行编码转换。 - `BufferedReader` 和 `BufferedWriter`:提供缓冲功能,提高读写...

    JAVA字符编码系列三[借鉴].pdf

    - Java中的`InputStreamReader`和`OutputStreamWriter`用于在字节流和字符流之间转换,可以指定字符编码。 - URL编码:URL中非ASCII字符需要使用`URLEncoder.encode()`进行编码,`URLDecoder.decode()`解码。 3. ...

    实验9 Java输入输出流.doc

    Java的I/O系统主要分为两大类:字节流(Byte Stream)和字符流(Character Stream)。字节流处理的是8位的字节数据,适用于处理任何类型的数据,包括图像、音频等二进制文件;而字符流则处理16位的Unicode字符,适合...

    java字节流和字符流

    Java中的字节流和字符流是IO操作中的两种基本类型,它们主要用于数据的输入和输出。字节流处理的数据单位是字节,而字符流处理的是Unicode字符。 字节流: 字节流主要由两个核心类构成:`InputStream`和`...

    java判断输入一段字符串是不是回文

    最好在myeclipse里面运行,可以直接在下面输入一段字符串,运行便可以看出结果。

    js判断输入字符串是否为空、空格、null的方法总结

    判断字符串是否为“空”字符即用户输入了空格 var strings = ' '; if (strings.replace(/(^s*)|(s*$)/g, ).length ==0) { alert('不能为空'); } 判断输入字符串是否为空或者全部都是空格 function is...

    JAVA中汉字字符转化为英文字符

    ### JAVA中汉字字符转化为英文字符 #### 知识点概览 本文将详细介绍如何在Java中实现汉字到英文字符的转换。此技术主要用于提取汉字的首字母或进行其他基于字符编码的操作。通过以下两个核心方法:`toTureAsciiStr`...

    java输入输出流的两种写法

    - `InputStreamReader` 和 `OutputStreamWriter` 可以将字节流转换为字符流,它们接受一个字节流作为参数并指定字符编码。 以上就是Java输入输出流的基本概念和常见写法。在实际应用中,根据需求选择合适的流类型...

    java输入输出流与文件处理

    字节流适用于处理二进制数据,而字符流更擅长处理文本数据,两者的区别在于数据处理的基本单位,字节流处理的是8位的数据,而字符流处理的是16位的Unicode字符。 #### 总结 Java的输入输出流与文件处理机制为...

    javaIO(java中的流的介绍)

    - InputStreamReader:从字节流转换为字符流,指定特定的字符编码。 - OutputStreamWriter:将字符流转换为字节流,同样可指定编码。 2. 流的过滤和包装 Java IO提供了过滤流(Filter Stream),可以对已有流...

    java中的各种输入流与输出流

    2. **字符流**:字符流处理Unicode字符,`Reader`是所有字符输入流的基类。` FileReader`从文件读取字符,`BufferedReader`提供缓冲区功能以提高读取性能。 3. **对象流**:`ObjectInputStream`用于反序列化Java...

    Java输入输出 Java输入输出

    java java输入输出 java输入流 java输出流 java java输入输出 java输入流 java输出流 java java输入输出 java输入流 java输出流

    JAVA输入输出流详细解读

    在实际的API实现中,`java.io`包内包含了两种类型的流:**字节流(Byte Stream)**和**字符流(Character Stream)**。字节流以`byte`为单位处理数据序列,适用于处理二进制文件;字符流则以`char`为单位,更适合...

Global site tag (gtag.js) - Google Analytics