android 自学日记(六) ——SAX解析中换行问题解决
16lz
2021-01-26
今天在写一个小项目的时候用到了SAX解析,遇到了一点小问题,网上找了好久都没有解决,最后还是自己发现了解决方法,特地和大家分享一下!
问题描述:
先来看下要解析XML文件:
<dict num="219" id="219" name="219"><key>big</key><ps>bɪg</ps><pron>http://res.iciba.com/resource/amp3/oxford/0/dd/3e/dd3eded85f7085dc92210aa496e92ba9.mp3</pron><ps>bɪɡ</ps><pron>http://res.iciba.com/resource/amp3/1/0/d8/61/d861877da56b8b4ceb35c8cbfdf65bb4.mp3</pron><pos>adj.</pos><acceptation>大的;重要的;(计划)庞大的;大方的;</acceptation><pos>adv.</pos><acceptation>大量地;成功地;夸大地;宽宏大量地;</acceptation><pos>n.</pos><acceptation>大亨;大公司;</acceptation><sent><orig>The word " big " has one syllable and the word " Africa " has three syllables.</orig><trans>“ big ” 这个词只有一个音节而 “ Africa ” 有三个音节.</trans></sent><sent><orig>There remains a tension at the heart of BIG s predicament.</orig><trans>目前,BIG心里仍维持着一种紧张的态势.</trans></sent><sent><orig>BIG might sell its Bsian arm to another insurance firm; Prudential was not the only bidder.</orig><trans>BIG也许会将其亚洲分支出售给另外一家保险公司, 保信并不是独一的竞价人.</trans></sent><sent><orig>Carrie fell for the wealthy Mr. Big.</orig><trans>Carrie爱上了那位富有的Big先生.</trans></sent><sent><orig>In this sentence, the word BIG is in capitals.</orig><trans>本句中BIG一词用的是大写字母.</trans></sent></dict>
大家都知道SAX解析的关键就是setContentHandler()方法中传入的DefaultHandler的子类,一开始我是这样写的:
public class WordsHandler extends DefaultHandler { //记录当前节点 private String nodeName; private Words words; //单词的词性与词义 private StringBuilder posAcceptation; //例句 private StringBuilder sent; /**获取解析后的words对象*/ public Words getWords() { return words; } //开始解析XML时调用 @Override public void startDocument() throws SAXException { //初始化 words = new Words(); posAcceptation = new StringBuilder(); sent = new StringBuilder(); } //结束解析XML时调用 @Override public void endDocument() throws SAXException { //将所有解析出来的内容赋予words words.setPosAcceptation(posAcceptation.toString().trim()); words.setSent(sent.toString().trim()); } //开始解析节点时调用 @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { nodeName = localName; } //结束解析节点时调用 @Override public void endElement(String uri, String localName, String qName) throws SAXException { } //获取节点中内容时调用 @Override public void characters(char[] ch, int start, int length) throws SAXException { String a = new String(ch, start, length); //去掉文本中原有的换行 for (int i = start; i < start + length; i++) { if (ch[i] == '\n') return; } //将节点的内容存入Words对象对应的属性中 if ("key".equals(nodeName)) { words.setKey(a); } else if ("ps".equals(nodeName)) { if (words.getPsE().length() <= 0) { words.setPsE(a); } else { words.setPsA(a); } } else if ("pron".equals(nodeName)) { if (words.getPronE().length() <= 0) { words.setPronE(a); } else { words.setPronA(a); } } else if ("pos".equals(nodeName)) { posAcceptation.append(a); } else if ("acceptation".equals(nodeName)) { posAcceptation.append(a); posAcceptation.append("\n");//重新排版换行 } else if ("orig".equals(nodeName)) { sent.append(a); sent.append("\n"); } else if ("trans".equals(nodeName)) { sent.append(a); sent.append("\n"); } else if ("fy".equals(nodeName)) { words.setFy(a); words.setIsChinese(true); } }}
这样的代码基本没什么问题,运行一下,大部分情况都能按照想要结果返回。
但是那,如果解析的字段中出现了“‘ < >这样的特殊符号时,就会发生问题 ,例如这句:
The word " big " has one syllable and the word " Africa " has three syllables.
运行后会发现解析结果是这样的:
The word " big " has one syllable and the word" Africa " has three syllables.
和想要的结果不一样,于是我找了好多资料,终于在理解的情况下解决了。
解决方法:
characters()方法在解析的时候会不停地调用,每次读取都是以 ’ ” < > 等这些字段为节点,上述情况就是因为characters()方法一次只读到文本中 ” 位置,并没有读完整个节点,因此添加了换行,而后再次调用characters()方法,读取的是“之后的内容,还是在这个节点中。正是这种同一个节点中不同地调用characters()方法,导致产生很多不需要的换行。
修改WordsHandler:
public class WordsHandler extends DefaultHandler { //记录当前节点 private String nodeName; private Words words; //单词的词性与词义 private StringBuilder posAcceptation; //例句 private StringBuilder sent; /**获取解析后的words对象*/ public Words getWords() { return words; } //开始解析XML时调用 @Override public void startDocument() throws SAXException { //初始化 words = new Words(); posAcceptation = new StringBuilder(); sent = new StringBuilder(); } //结束解析XML时调用 @Override public void endDocument() throws SAXException { //将所有解析出来的内容赋予words words.setPosAcceptation(posAcceptation.toString().trim()); words.setSent(sent.toString().trim()); } //开始解析节点时调用 @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { nodeName = localName; } //结束解析节点时调用 @Override public void endElement(String uri, String localName, String qName) throws SAXException { //在读完整个节点后换行 if("acceptation".equals(localName)){ posAcceptation.append("\n"); }else if("orig".equals(localName)){ sent.append("\n"); }else if("trans".equals(localName)){ sent.append("\n"); sent.append("\n"); } } //获取节点中内容时调用 @Override public void characters(char[] ch, int start, int length) throws SAXException { String a = new String(ch, start, length); //去掉文本中原有的换行 for (int i = start; i < start + length; i++) { if (ch[i] == '\n') return; } //将节点的内容存入Words对象对应的属性中 if ("key".equals(nodeName)) { words.setKey(a); } else if ("ps".equals(nodeName)) { if (words.getPsE().length() <= 0) { words.setPsE(a); } else { words.setPsA(a); } } else if ("pron".equals(nodeName)) { if (words.getPronE().length() <= 0) { words.setPronE(a); } else { words.setPronA(a); } } else if ("pos".equals(nodeName)) { posAcceptation.append(a); } else if ("acceptation".equals(nodeName)) { posAcceptation.append(a); } else if ("orig".equals(nodeName)) { sent.append(a); } else if ("trans".equals(nodeName)) { sent.append(a); } else if ("fy".equals(nodeName)) { words.setFy(a); words.setIsChinese(true); } }}
仅仅是把重新排版的过程放到了endElement()方法执行,此方法是在节点结束时调用,即读到 </xxx>
这个标志时, 在这个方法中添加我们想要的换行就可以了。
上述讲到的那句:The word ” big ” has one syllable and the word ” Africa ” has three syllables.是在一个<org>
</org>
中的,因此也只会在读取到 </org>
后才换行,达到我们想要的换行方式。
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- Android(安卓)ApiDemos示例解析(127):Views->Layout Animation->
- Android(安卓)AsyncTask源码解析
- Android(安卓)Manifest.xml文件解析
- 三种解析xml的方式
- Android(安卓)约束布局ConstraintLayout解析
- Android(安卓)布局优化之include与merge,最后有ViewStub
- Android(安卓)AsyncTask 详细解析
- Android多线程(二)消息处理机制---Handler、Message、Looper源码原