Java中的SGML解析器?

我正在寻找一种Java解析器,它可以解析用SGML格式化的文档。

对于重复的监视器:我知道讨论这个主题的另外两个线程: 使用SGML Java SGML 解析Java String 到XML转换? 但两者都没有解决方案,因此也是新主题。

对于那些将XML与SGML混淆的人:请阅读: http : //www.w3.org/TR/NOTE-sgml-xml-971215#null (简而言之,有足够的细微差别至少使它无法使用它香草forms)

对于那些喜欢向Google报道海报的人:我已经做过,而我能提出的最接近的是广受欢迎的SAXParser: http : //download.oracle.com/javase/1.4.2/docs/api/javax /xml/parsers/SAXParser.html但这当然是一个XML解析器。 我正在四处寻找是否有人实施了SAX Parser的修改以适应SGML。

最后,我不能使用SX,因为我正在寻找Java解决方案。

谢谢! 🙂

我有一些解决这个问题的方法

第一个是你做的 – 检查sgml文档是否足够接近XML,以使标准SAX解析器工作。

第二个是对HTML解析器做同样的事情。 这里的技巧是找到一个不忽略非HTML元素的方法。

在搜索“sgml parser Java”时,我确实找到了一些Java SGML解析器,更多的是在acedemia中。 我不知道他们的工作有多好。

最后一步是采用标准(非Java)SGML解析器并将文档转换为可以用Java读取的内容。

看起来你能够完成第一步。

目前没有使用Java解析SGML的API。 还没有用于将SGML转换为XML然后使用Java解析它的任何api或库。 对于我迄今为止所做的所有项目,SGML的状态被XML取代,我认为在这个领域不会有任何工作,但这只是猜测。

这是来自大学的一些开源代码代码,但我还没有尝试过,你必须搜索才能找到其他依赖类。 我相信Java中唯一可行的解​​决方案需要正则表达式。

此外,这是一个公共SGML / XML软件的链接 。

Java SE在javax.swing.text.html.parser包中包含一个HTML解析器。 它在其文档中声称是一般的SGML解析器,但随后在文档中反驳说,您应该只将它与提供的HTML DTD类一起使用。

如果您将其置于宽松模式并且您的SGML文档没有很多隐含的结束标记,您可能会得到合理的结果。

阅读JavaDoc中的解析器,请访问: http : //docs.oracle.com/javase/6/docs/api/javax/swing/text/html/parser/DocumentParser.html

创建一个这样的实例:

 new DocumentParser(DTD.getDTD("html32")) 

或者您可以忽略使用带DocumentParser的自定义DTD的警告,并创建与您自己的SGML格式的规则匹配的DTD子类。

这显然不是一个工业强度的SGML解析器,但它应该是一次性数据迁移工作的良好起点。 我发现它在以前解析HTML的项目中很有用。

我通过JNI使用OpenSP,因为它似乎没有纯Java SGML解析器。 我写了一个类似SAX的实验包装器,可以在http://sourceforge.net/projects/sasgml上找到 (当然,它有JNI的所有缺点……但是对我的要求来说已经足够了)。

另一种方法是使用Open SP中的 sx将文档转换为XML,然后运行传统的SAX解析器。

如果您正在解析它的HTML,这可能会:

http://ccil.org/~cowan/XML/tagsoup/

虽然它是一个非常古老的post,但我并没有声称我提供的答案是完美的,但它符合我的目的。 所以我保留这个我用堆栈编写的代码,以便在我的情况下以某种方式获取数据。 我希望它可能对其他人有所帮助。

 try (BufferedReader br = new BufferedReader(new FileReader(new File( fileName)))) { while ((line = br.readLine()) != null) { line = line.trim(); int startOfTag = line.indexOf("<"); int endOfTag = line.indexOf(">"); String currentTag = ""; if (startOfTag > -1 && endOfTag > -1) { if (countStart) headerTagsCounter++; currentTag = line.substring(startOfTag + 1, endOfTag); String currentData = line.substring(endOfTag + 1, line.length()); if (i == 1) { tagStack.push(currentTag); i++; } if (currentData.isEmpty() || currentData == "") {//If there is no data, its a parent tag... if (!currentTag.contains("/")) {// if its an opening tag... switch (currentTag) {// these tags are useless in my case, so just skipping these tags. case "CORRECTION": case "PAPER": case "PRIVATE-TO-PUBLIC": case "DELETION": case "CONFIRMING-COPY": case "CAPTION": case "STUB": case "COLUMN": case "TABLE-FOOTNOTES-SECTION": case "FOOTNOTES": case "PAGE": break; default: { countStart = false; int tagCounterNumber = 0; String historyTagToRemove = ""; for (String historyTag : historyStack) { String tagCounter = ""; if (historyTag.contains(currentTag)) {//if it's a repeating tag..Append the counter and update the same in history tag.. historyTagToRemove = historyTag; if (historyTag .equalsIgnoreCase(currentTag)) { tagCounterNumber = 1; } else if (historyTag.length() > currentTag .length()) { tagCounter = historyTag .substring(currentTag .length()); if (tagCounter != null && !tagCounter.isEmpty()) { tagCounterNumber = Integer .parseInt(tagCounter) + 1; } } } } if (tagCounterNumber > 0) currentTag += tagCounterNumber; if (historyTagToRemove != null && !historyTagToRemove.isEmpty()) { historyStack.remove(historyTagToRemove); historyStack.push(currentTag); } tagStack.push(currentTag); break; } } } else// if its end of a tag... Match the current tag with top of stack and if its a match, pop it out { currentTag = currentTag.substring(1); String tagRemoved = ""; String topStackTag = tagStack.lastElement(); if (topStackTag.contains(currentTag)) { tagRemoved = tagStack.pop(); historyStack.push(tagRemoved); } if (tagStack.size() < 2) cik = ""; if (tagStack.size() == 2 && cik != null && !cik.isEmpty()) for (int j = headerTagsCounter - 1; j < tagList.size(); j++) { String item = tagList.get(j); if (!item.contains("@@")) { item += "@@" + cik; tagList.remove(j); tagList.add(j, item); } } } } else {// if current tag has some data... currentData = currentData.trim(); String stackValue = ""; for (String tag : tagStack) { if (stackValue != null && !stackValue.isEmpty() && stackValue != "") stackValue = stackValue + "||" + tag; else stackValue = tag; } switch (currentTag) { case "ACCESSION-NUMBER": accessionNumber = currentData; break; case "FILING-DATE": dateFiled = currentData; break; case "TYPE": formType = currentData; break; case "CIK": cik = currentData; break; } tagList.add(stackValue + "$$" + currentTag + "::"+ currentData); } } } // Now all your data is available with in tagList, stack is separated by ||, key is separated by $$ and value is separated by :: } } catch (Exception e) { // TODO Auto-generated catch block } } 

输出:

文件来源: http : /10k-staging.s3.amazonaws.com/edgar0105/2016/12/20/935015/000119312516799070/0001193125-16-799070.hdr.sgml

输出代码:

 SEC-HEADER$$SEC-HEADER::0001193125-16-799070.hdr.sgml : 20161220 SEC-HEADER$$ACCEPTANCE-DATETIME::20161220172458 SEC-HEADER$$ACCESSION-NUMBER::0001193125-16-799070 SEC-HEADER$$TYPE::485APOS SEC-HEADER$$PUBLIC-DOCUMENT-COUNT::9 SEC-HEADER$$FILING-DATE::20161220 SEC-HEADER$$DATE-OF-FILING-DATE-CHANGE::20161220 SEC-HEADER||FILER||COMPANY-DATA$$CONFORMED-NAME::ARTISAN PARTNERS FUNDS INC@@0000935015 SEC-HEADER||FILER||COMPANY-DATA$$CIK::0000935015@@0000935015 SEC-HEADER||FILER||COMPANY-DATA$$IRS-NUMBER::391811840@@0000935015 SEC-HEADER||FILER||COMPANY-DATA$$STATE-OF-INCORPORATION::WI@@0000935015 SEC-HEADER||FILER||COMPANY-DATA$$FISCAL-YEAR-END::0930@@0000935015 SEC-HEADER||FILER||FILING-VALUES$$FORM-TYPE::485APOS@@0000935015 SEC-HEADER||FILER||FILING-VALUES$$ACT::33@@0000935015 SEC-HEADER||FILER||FILING-VALUES$$FILE-NUMBER::033-88316@@0000935015 SEC-HEADER||FILER||FILING-VALUES$$FILM-NUMBER::162062197@@0000935015 SEC-HEADER||FILER||BUSINESS-ADDRESS$$STREET1::875 EAST WISCONSIN AVE STE 800@@0000935015 SEC-HEADER||FILER||BUSINESS-ADDRESS$$CITY::MILWAUKEE@@0000935015 SEC-HEADER||FILER||BUSINESS-ADDRESS$$STATE::WI@@0000935015 SEC-HEADER||FILER||BUSINESS-ADDRESS$$ZIP::53202@@0000935015 SEC-HEADER||FILER||BUSINESS-ADDRESS$$PHONE::414-390-6100@@0000935015 SEC-HEADER||FILER||MAIL-ADDRESS$$STREET1::875 EAST WISCONSIN AVE STE 800@@0000935015 SEC-HEADER||FILER||MAIL-ADDRESS$$CITY::MILWAUKEE@@0000935015 SEC-HEADER||FILER||MAIL-ADDRESS$$STATE::WI@@0000935015 SEC-HEADER||FILER||MAIL-ADDRESS$$ZIP::53202@@0000935015 SEC-HEADER||FILER||FORMER-COMPANY$$FORMER-CONFORMED-NAME::ARTISAN FUNDS INC@@0000935015 SEC-HEADER||FILER||FORMER-COMPANY$$DATE-CHANGED::19950310@@0000935015 SEC-HEADER||FILER||FORMER-COMPANY1$$FORMER-CONFORMED-NAME::ZIEGLER FUNDS INC@@0000935015 SEC-HEADER||FILER||FORMER-COMPANY1$$DATE-CHANGED::19950109@@0000935015 SEC-HEADER||FILER1||COMPANY-DATA1$$CONFORMED-NAME::ARTISAN PARTNERS FUNDS INC@@0000935015 SEC-HEADER||FILER1||COMPANY-DATA1$$CIK::0000935015@@0000935015 SEC-HEADER||FILER1||COMPANY-DATA1$$IRS-NUMBER::391811840@@0000935015 SEC-HEADER||FILER1||COMPANY-DATA1$$STATE-OF-INCORPORATION::WI@@0000935015 SEC-HEADER||FILER1||COMPANY-DATA1$$FISCAL-YEAR-END::0930@@0000935015 SEC-HEADER||FILER1||FILING-VALUES1$$FORM-TYPE::485APOS@@0000935015 SEC-HEADER||FILER1||FILING-VALUES1$$ACT::40@@0000935015 SEC-HEADER||FILER1||FILING-VALUES1$$FILE-NUMBER::811-08932@@0000935015 SEC-HEADER||FILER1||FILING-VALUES1$$FILM-NUMBER::162062198@@0000935015 SEC-HEADER||FILER1||BUSINESS-ADDRESS1$$STREET1::875 EAST WISCONSIN AVE STE 800@@0000935015 SEC-HEADER||FILER1||BUSINESS-ADDRESS1$$CITY::MILWAUKEE@@0000935015 SEC-HEADER||FILER1||BUSINESS-ADDRESS1$$STATE::WI@@0000935015 SEC-HEADER||FILER1||BUSINESS-ADDRESS1$$ZIP::53202@@0000935015 SEC-HEADER||FILER1||BUSINESS-ADDRESS1$$PHONE::414-390-6100@@0000935015 SEC-HEADER||FILER1||MAIL-ADDRESS1$$STREET1::875 EAST WISCONSIN AVE STE 800@@0000935015 SEC-HEADER||FILER1||MAIL-ADDRESS1$$CITY::MILWAUKEE@@0000935015 SEC-HEADER||FILER1||MAIL-ADDRESS1$$STATE::WI@@0000935015 SEC-HEADER||FILER1||MAIL-ADDRESS1$$ZIP::53202@@0000935015 SEC-HEADER||FILER1||FORMER-COMPANY2$$FORMER-CONFORMED-NAME::ARTISAN FUNDS INC@@0000935015 SEC-HEADER||FILER1||FORMER-COMPANY2$$DATE-CHANGED::19950310@@0000935015 SEC-HEADER||FILER1||FORMER-COMPANY3$$FORMER-CONFORMED-NAME::ZIEGLER FUNDS INC@@0000935015 SEC-HEADER||FILER1||FORMER-COMPANY3$$DATE-CHANGED::19950109@@0000935015 SEC-HEADER||SERIES-AND-CLASSES-CONTRACTS-DATA||NEW-SERIES-AND-CLASSES-CONTRACTS$$OWNER-CIK::0000935015 SEC-HEADER||SERIES-AND-CLASSES-CONTRACTS-DATA||NEW-SERIES-AND-CLASSES-CONTRACTS||NEW-SERIES$$SERIES-ID::S000056665 SEC-HEADER||SERIES-AND-CLASSES-CONTRACTS-DATA||NEW-SERIES-AND-CLASSES-CONTRACTS||NEW-SERIES$$SERIES-NAME::Artisan Thematic Fund SEC-HEADER||SERIES-AND-CLASSES-CONTRACTS-DATA||NEW-SERIES-AND-CLASSES-CONTRACTS||NEW-SERIES||CLASS-CONTRACT$$CLASS-CONTRACT-ID::C000179292 SEC-HEADER||SERIES-AND-CLASSES-CONTRACTS-DATA||NEW-SERIES-AND-CLASSES-CONTRACTS||NEW-SERIES||CLASS-CONTRACT$$CLASS-CONTRACT-NAME::Investor Shares