使用java Pattern和Matcher,如何获得第一个匹配的标记内容

时间:2013-11-25 16:57:49

标签: java regex cpu matcher

我在SoapMessage看起来像这样:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
    <Action xmlns="http://www.w3.org/2005/08/addressing">http://service.xxx.dk/DialogModtag</Action>
    <MessageID xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:382b4943-26e8-4698-a275-c3149d2d889e</MessageID>
    <To xmlns="http://www.w3.org/2005/08/addressing">http://xxx.dk/12345678</To>
    <RelatesTo xmlns="http://www.w3.org/2005/08/addressing">uuid:cb2320dc-c8ab-4880-94cb-2ab68129216f</RelatesTo>
</soap:Header>
<soap:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-2515">
    Some content ...
</soap:Body>

我试图提取&lt; Action&gt;的内容&lt; Header&gt;中的标记标记使用这样的代码:

Pattern PATTERN_SOAP_ACTION = 
    Pattern.compile(".*Header.*Action.*>(.*)<.*Action.*Header.*", Pattern.DOTALL);

String text = readFile("c:\\temp\\DialogUdenBilag.xml");
Matcher matcherSoapAction = PATTERN_SOAP_ACTION.matcher(text);
if (matcherSoapAction.matches()) { System.out.println(matcherSoapAction.group(1)); }
else { System.out.println("SaopAction not found"); }

这似乎对小肥皂消息有效。但是当soap:Body增长到+ 1MB时,则matches()函数调用需要几分钟才能完成。

任何让我的正则表达式模式更加CPU友好的想法?

2 个答案:

答案 0 :(得分:2)

解决方案

您希望使用XML解析器来获得更加CPU友好的解决方案。

 XMLInputFactory factory = XMLInputFactory.newInstance();
 XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("c:\\temp\\DialogUdenBilag.xml"));

 boolean found=false;
 boolean inHeader=false;
 String actionContent = "";

 while(!found && reader.hasNext()){
    if(reader.next() == XMLStreamConstants.START_ELEMENT) {
        String localName=reader.getLocalName());

        if ("Header".equalsIgnoreCase(localName) {
            inHeader = true;
        }

        if(inHeader && "Action".equalsIgnoreCase(localName) {

            int evt=reader.next();
            do {
               if (evt==XMLStreamConstants.CHARACTERS) {
                   actionContent = reader.getText().trim();
                   found=true;
                   break;
               }

               evt=reader.next();
            } while(evt != XMLStreamConstants.END_ELEMENT);

        }
    }
 }

 if (found) {
     System.out.println(actionContent);
 } else {
     System.out.println("SaopAction not found");
 }

讨论

这个小片段有点冗长但你会得到你的答案而不是查看整个 XML代码。实际上,代码段会在找到soap:Action标记后停止,然后返回此代码的文本内容。

答案 1 :(得分:1)

使用正则表达式来解析XML是邪恶的,may incur the Wrath of the One whose Name cannot be expressed in the Basic Multilingual Plane.如果需要解析XML,请使用实际的XML解析器 - 这就是它的用途。像这样的情况也是XPath表达式的用法:

javax.xml.xpath.XPath xpath = javax.xml.xpath.XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(new NamespaceContextMap(
    "s", "http://schemas.xmlsoap.org/soap/envelope/",
    "a", "http://www.w3.org/2005/08/addressing"));
javax.xml.xpath.XPathExpression expression = xpath.compile("//s:Header/a:Action");
String result = expression.evaluate(new org.xml.sax.InputSource(new FileReader("c:\\temp\\DialogUdenBilag.xml")));

(请注意,NamespaceContextMap不是标准类 - 有关实施,请参阅here。)


至于你的正则表达式:它被编写为不必要地匹配整个输入字符串,并进行大量的最大匹配而不是最小匹配。如果你的表达式更紧密地关注文档的相关位(例如,"<((?:\\w+:)?)?Header\\b[^>]*>.*?<((?:\\w+:)?)Action\\b[^>]*>(.*?)</\\2Action>.*?</\\1Header>"),并且调用Matcher.find()来进行子串匹配,那么你可以通过少量CPU来咀嚼。也就是说,用regexp解析XML是不好的做法 - 你真的应该使用XML解析器了!