错误:
character mismatch (new: 军 [0x519b] != old: [0xbe¾]) for encoding change from ISO-8859-1 to GB2312 at character offset 186。
原因:
如果请求url返回的页面上中文的title写在了meta的前面,而且这个meta里设置的charset编码信息又和parser默认的或者使用者自己设置定编码不一样。
那么就会报这个错,但是如果meta之前没有任何中文,那么就不会报这个错。因为htmlparser本身会根据html源码中的charset信息设置编码:<原创网址:http://hi.csdn.net/space-8079523.html,作者:蛰伏神兽>
让我们就根据报出的错误栈来看下htmlparser源码,来看看到底是怎么回事:
org.htmlparser.util.EncodingChangeException: character mismatch (new: 军 [0x519b] != old: [0xbe¾]) for encoding change from ISO-8859-1 to GB2312 at character offset 186
at org.htmlparser.lexer.InputStreamSource.setEncoding(InputStreamSource.java:279)
at org.htmlparser.lexer.Page.setEncoding(Page.java:864)
at org.htmlparser.tags.MetaTag.doSemanticAction(MetaTag.java:149)
at org.htmlparser.scanners.TagScanner.scan(TagScanner.java:68)
at org.htmlparser.scanners.CompositeTagScanner.scan(CompositeTagScanner.java:159)
at org.htmlparser.util.IteratorImpl.nextNode(IteratorImpl.java:91)
at org.htmlparser.Parser.parse(Parser.java:700)
先来看下parser解析Node的流程,
parser会用IteratorImpl的nextNode()对指定的html源码一行一行的往下解析然后一个一个产生node,没产生一个Node就会用这个Node的扫描器扫描这个Node来构建子节点,或者执行一些操作。当遇到meta标签的时候他就会产生一个MetaTag标签,并且用对应Scanner(TagScanner)扫描这个标签:
public Tag scan (Tag tag, Lexer lexer, NodeList stack) throws ParserException
{
tag.doSemanticAction ();
return (tag);
}
这个方法中科院看到实际上这个方法就是执行了一下MetaTag的doSemanticAction();方法:
public void doSemanticAction ()
throws
ParserException
{
String httpEquiv;
String charset;
httpEquiv = getHttpEquiv ();//原创网址:http://hi.csdn.net/space-8079523.html,作者:蛰伏神兽
if ("Content-Type".equalsIgnoreCase (httpEquiv))
{
charset = getPage ().getCharset (getAttribute ("CONTENT"));
getPage ().setEncoding (charset);
}
}
很明显这个方法的作用就是获取刚刚产生的MetaTag节点中的Content信息,编码信息,然后设置当前页面的编码。
也就是说htmlparser其实有自动搜索页面源码的编码的能力,<原创网址:http://hi.csdn.net/space-8079523.html,作者:蛰伏神兽
>在遍历整个html源码信息,一个一个产生Node的时候就开始设置了。
但是这必须要执行到拥有编码信息的Meta行才会发现这个页面的编码。那么也就是说之前用来解析的默认编码集很有可能和这个html页面编码是不一致的。
我们再来看看出错源的那段代码:
public void setEncoding (String character_set)
throws
ParserException
{
String encoding;
InputStream stream;
char[] buffer;
int offset;
char[] new_chars;
encoding = getEncoding ();
if (!encoding.equalsIgnoreCase (character_set)) //绿色
{
stream = getStream ();
try
{
buffer = mBuffer;
offset = mOffset;
stream.reset ();
try
{
mEncoding = character_set;
mReader = new InputStreamReader (stream, character_set);
mBuffer = new char[mBuffer.length];
mLevel = 0;
mOffset = 0;
mMark = -1;
if (0 != offset)
{
new_chars = new char[offset];
if (offset != read (new_chars))
throw new ParserException ("reset stream failed");
for (int i = 0; i < offset; i++)
if(new_chars[i]!=buffer[i])
throw new EncodingChangeException ("character mismatch (new: "
+ new_chars[i]
+ " [0x"
+ Integer.toString (new_chars[i], 16)
+ "] != old: "
+ " [0x"
+ Integer.toString (buffer[i], 16)
+ buffer[i]
+ "]) for encoding change from "
+ encoding
+ " to "
+ character_set
+ " at character offset "
+ i);
}
}
catch (IOException ioe)
{
throw new ParserException (ioe.getMessage (), ioe);
}
}
catch (IOException ioe)
{ // bug #1044707 mark()/reset() issues
throw new ParserException ("Stream reset failed ("
+ ioe.getMessage ()
+ "), try wrapping it with a org.htmlparser.lexer.Stream",
ioe);
}
}
}
之前MetaTag的doSematicAction();中的setEcoding(charset)最终执行的代码就是这一部分。<原创网址:http://hi.csdn.net/space-8079523.html,作者:蛰伏神兽
if(new_chars[i]!=buffer[i])
先后两种编码集转换的字符串要不一致才会报错,如果meta信息之前全是英文那肯定一致,也就不会报错了。
说到这里应该会有人和我一样有个疑问了:htmlparser的作者们为什么要设置这个错误类型呢?<原创网址:http://hi.csdn.net/space-8079523.html,作者:蛰伏神兽
>两种不同编码集转换的源代码,如果是中文的话肯定会不一致!
感觉这个错误类型完全没什么用啊。所以我解决这个错误的方法就是修改这部分源码,把这一段给注释掉~(就让他不一样吧,肯定不一样的啦。因为编码不同啦,所以不要报错啦):修改后代码:
public void setEncoding (String character_set)
throws
ParserException
{
String encoding;
InputStream stream;
char[] buffer;
int offset;
char[] new_chars;
encoding = getEncoding ();
if (!encoding.equalsIgnoreCase (character_set))
{
stream = getStream ();
try
{
buffer = mBuffer;
offset = mOffset;
stream.reset ();
try
{
mEncoding = character_set;
mReader = new InputStreamReader (stream, character_set);
mBuffer = new char[mBuffer.length];
mLevel = 0;
mOffset = 0;
mMark = -1;
if (0 != offset)
{
new_chars = new char[offset];
if (offset != read (new_chars))
throw new ParserException ("reset stream failed");
/*for (int i = 0; i < offset; i++)
if (new_chars[i] != buffer[i])
throw new EncodingChangeException ("character mismatch (new: "
+ new_chars[i]
+ " [0x"
+ Integer.toString (new_chars[i], 16)
+ "] != old: "
+ " [0x"
+ Integer.toString (buffer[i], 16)
+ buffer[i]
+ "]) for encoding change from "
+ encoding
+ " to "
+ character_set
+ " at character offset "
+ i);*/
}
}
catch (IOException ioe)
{
throw new ParserException (ioe.getMessage (), ioe);
}
}
catch (IOException ioe)
{ // bug #1044707 mark()/reset() issues
throw new ParserException ("Stream reset failed ("
+ ioe.getMessage ()
+ "), try wrapping it with a org.htmlparser.lexer.Stream",
ioe);
}//原创网址:http://hi.csdn.net/space-8079523.html,作者:蛰伏神兽
}
}
好了.这样就行了,不管meta在title之前还是title之后都没关系了。htmlparser都会自动获取charset信息并且更改默认编码了。
大家有不同的看法欢迎留言讨论.转载请注明出处!谢谢啦。