Java中的SGML解析器?

20

我正在寻找一个能够解析SGML格式文档的Java解析器。

对于重复的显示器: 我知道还有其他两个讨论此主题的帖子: Parsing Java String with SGML Java SGML to XML conversion? 但没有一个能够解决这个问题,因此我开了一个新的话题。

对于将XML与SGML混淆的人: 请阅读此文:http://www.w3.org/TR/NOTE-sgml-xml-971215#null (简而言之,有足够微妙的区别至少使其以原始形式无法使用)

对于喜欢让发帖者谷歌它的人: 我已经这样做了,最接近的结果是广受欢迎的SAXParser:http://download.oracle.com/javase/1.4.2/docs/api/javax/xml/parsers/SAXParser.html 但当然这是用来作为XML解析器的。我正在寻找是否有人实现了SAX解析器的修改版本以适应SGML。

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

谢谢!:)


1
人们还在使用SGML吗?我真的很好奇 - 在你的情况下它用于什么? - skaffman
我大约有2500份格式为SGML的文件 - 我需要使用这些数据进行一些统计分析。我正在编写一个快速程序来确定标签的分布情况,我查看了其中的一些文件,它们似乎只使用了几个选择性标签。在这种情况下,我可以轻松地使用SAX解析器。 - user183037
我有成千上万个SGML文件,而且还在不断制作更多。尽管不太时髦,但SGML在出版行业仍然被广泛使用! - Woody
幸运的是,我的一堆标签符合XML标签的描述,所以我能够使用SAXParser。(相比于XMLReader,它更容易使用 - 实现XMLReader的示例令人惊讶地稀少。) - user183037
3
等等——最后一个评论说“我能够使用SAX解析器。”那么你已经找到答案了,是吗?为什么不写一个答案并标记为已完成? - Charlie Martin
1
是的和不是。我一开始是在寻找一个SGML分析器,但现在使用一个SAX分析器只是一个权宜之计,因为它适合我的当前文档集,而不是解决方案。 - user183037
6个回答

4
我有几种方法来解决这个问题。
第一种方法是你所做的——检查sgml文档是否足够接近XML,使标准SAX解析器能够工作。
第二种方法是使用HTML解析器完成同样的任务。关键在于找到一个不会忽略非HTML元素的解析器。
我在搜索"sgml parser Java"时发现了一些Java SGML解析器,主要在学术界。我不知道它们的工作效果如何。
最后一步是使用标准(非Java)SGML解析器并将文档转换为可以在Java中读取的格式。
看起来你已经成功完成了第一步。

1
我没有找到任何Java SGML解析器的资源 - 如果您再次找到它,能否请您发布链接 - 谢谢! - user183037
实际上,当我重新查看时,我也找不到任何东西。 - Kathy Van Stone

2
我使用OpenSP通过JNI,因为似乎没有纯Java SGML解析器。我编写了一个实验性的类似SAX的包装器,可在http://sourceforge.net/projects/sasgml上获得(当然,它具有JNI的所有缺点...但对我的要求已足够)。
另一种方法是使用来自Open SP的sx将文档转换为XML,然后运行传统的SAX解析器。

1

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"))

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

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


1

目前没有适用于Java的解析SGML的API。同样也没有将SGML转换为XML,然后再使用Java解析它的API或库。考虑到在我之前工作的所有项目中,SGML的地位已被XML取代,我不认为会有人在这个领域做出任何工作,但这只是一个猜测。

此处有来自一所大学的开源代码可以完成此功能,但我没有尝试过,您需要搜索以找到其他相关类。我相信在Java中唯一可行的解决方案需要正则表达式。

此外,此链接提供了一些公共的SGML/XML软件。


第一个链接已经过时。 - Tsundoku

0

虽然这是一个非常古老的帖子,我并不声称我提供的答案是完美的,但它满足了我的需求。因此,我将保留我编写的使用堆栈获取数据的代码,以便按照我的要求进行操作。我希望它对其他人有所帮助。

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

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接