近日在开发爬虫程序时发现,如果事先不指定正确的字符集编码,在得到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,发现也是无法提供一个正确的,好用的方法来判断字节流的编码格式。
分享到:
相关推荐
从最初的字节流到后来的字符流乃至NIO的引入,每一步的发展都反映了Java语言设计者们对于高效、灵活且易于使用的I/O系统的不懈追求。理解这些发展历程有助于开发者更好地掌握Java I/O系统的核心概念和技术细节,从而...
输入流(InputStream)和输出流(OutputStream)构成了Java IO的基本骨架。它们是所有字节流的抽象基类,提供了读写字节的基本操作。例如,FileInputStream和FileOutputStream分别用于读取和写入文件。对于字符流,...
9.2.4 判断字符串的开头和结尾 225 9.2.5 分割字符串 225 9.2.6 在字符串中查找子字符串或字符 226 9.2.7 替换字符串中的内容 226 9.2.8 String对象——磐石刻字 227 9.3 String类的最佳拍档——StringBuffer类...
9.2.4 判断字符串的开头和结尾 225 9.2.5 分割字符串 225 9.2.6 在字符串中查找子字符串或字符 226 9.2.7 替换字符串中的内容 226 9.2.8 String对象——磐石刻字 227 9.3 String类的最佳拍档——StringBuffer类...
然而,随着Unicode的广泛使用,Java在后续版本中增加了面向Unicode字符的类库,以支持更广泛的文本编码。在JDK 1.4中引入了NIO(New I/O),这是一个性能优化的I/O模型,提供非阻塞I/O操作,以适应高并发和大数据...
13.2.1 一段令人困惑的字符串程序 248 13.2.2 “一次投入,终身回报”的String内存机制 249 13.2.3 String对象特殊机制付出的代价 252 13.3 StringBuffer类 253 13.3.1 弥补String不足的StringBuffer类 ...
**1.1 Java的困惑** 随着互联网技术的发展,传统的Java开发方式面临着诸多挑战。Java虽然功能强大且成熟稳定,但在快速迭代、灵活应对需求变化方面显得力不足。开发者在进行Web应用开发时常常需要面对复杂的框架...