java - 从文件中读取字符串并将其转换为文字正则表达式

时间:2014-02-11 22:44:54

标签: java regex

我有这段代码,我将一个Pattern键和一个String标记插入一个hashmap:

while( (word = reservedWordsRead.readLine()) != null ) {
    String[] k = word.split(" ");
    infoList.put(Pattern.compile("^("+k[0]+")"), //lexeme
                        k[1]); //token
}

它从一个如下文件中读取:

) rparen
( lparen

但是括号无法识别,因此我将文件修改为如下所示:

\\) rparen
\\( lparen

和这样的代码:

while( (word = reservedWordsRead.readLine()) != null ) {
    String[] k = word.split(" ");
    infoList.put(Pattern.compile("^("+Pattern.quote(k[0])+")"), //lexeme
                        k[1]); //token
}

但我没有得到正确的输出。它与任何东西都不匹配。此外,rparen和lparen被插入到hashmap中,因为我能够使用我的tokenizer()方法打印以下内容:

pattern: ^(\Q\\)\E), token: rparen
pattern: ^(\Q\\(\E), token: lparen

这是我的标记器方法:

public void tokenize(String str) {
    String s = str.trim();
    tokenList.clear();

    while (!s.equals("")) {
        boolean match = false;
        for ( Entry<Pattern,String> thing: infoList.entrySet() ) {
            System.out.println("pattern: "+thing.getKey().toString()+", token: "+thing.getValue());
            Matcher m = thing.getKey().matcher(s);
            if (m.find()) {
                match = true;
                String tok = m.group().trim();
                s = m.replaceFirst("").trim();
                tokenList.put(tok,thing.getValue());
                break;
            }
        } if (!match) 
            throw new ParserException("Unexpected character in input: "+s);
    }
}

我不确定我做错了什么..非常感谢你的帮助:)。

2 个答案:

答案 0 :(得分:1)

如果您想匹配完整的字符串,则应使用Pattern.quote()

你遇到的问题是你试图引用传递的字符串转义括号,基本上是双转义(让人联想到HTML中的&amp;amp;)。虽然可以将所有特殊转义字符放在输入文件中,但为什么要这么麻烦?让Pattern为您完成工作。

这是一个测试,我们尝试使用几个不同的输入并尝试将它们变成Pattern,就像你一样。

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class RegexTest
{
    private static final String[] TESTS = {"a","(","\\(","\\\\(","[letters]"};

    public static void main(String[] args) {
        for(String test : TESTS) {
            examineRegex(test);
            System.out.println();
        }
    }

    public static void examineRegex(String match) {
        System.out.println("Testing "+match);
        String template = "^(%s)";
        String regex = String.format(template, match);
        examinePattern(match, regex);
        String quotedRegex = String.format(template, Pattern.quote(match));
        examinePattern(match, quotedRegex);
    }

    public static void examinePattern(String match, String regex) {
        try {
            Pattern pattern = Pattern.compile(regex);
            System.out.println("  Compiled:  "+pattern);
            System.out.println("  Match?:    "+pattern.matcher(match).matches());
        } catch (PatternSyntaxException e) {
            System.out.println("  Failed to compile: "+e.getMessage()
                .substring(0, e.getMessage().indexOf('\n')));
        }
    }
}

此程序的输出如下(注释内联):

Testing a
  Compiled:  ^(a)
  Match?:    true
  Compiled:  ^(\Qa\E)
  Match?:    true

对于“普通”字符串的简单情况,原始方法和使用Pattern.quote()都有效。到目前为止一切都很好。

Testing (
  Failed to compile: Unclosed group near index 4
  Compiled:  ^(\Q(\E)
  Match?:    true

但是如果我们传入一个构造,例如(,我们会收到一个错误,除非我们引用它。

Testing \(
  Compiled:  ^(\()
  Match?:    false
  Compiled:  ^(\Q\(\E)
  Match?:    true

如果我们传入一个转义构造,原始模式会成功编译,但它与输入字符串不匹配。那不是世界末日 - 它会匹配( - 但这是违反直觉的;它破坏了传入的东西是我们匹配的期望。

Testing \\(
  Failed to compile: Unclosed group near index 6
  Compiled:  ^(\Q\\(\E)
  Match?:    true

现在我们双重逃避模式,就像试图将输入视为Java字符串一样。这表明在尝试确定需要转移多少时可能会产生混淆。

Testing [letters]
  Compiled:  ^([letters])
  Match?:    false
  Compiled:  ^(\Q[letters]\E)
  Match?:    true

最后,假设我们想匹配一个也是实际正则表达式的字符串?它将成功编译,因此无法提醒我们该问题,但将无法匹配预期的字符串。

如您所见,Pattern.quote()每次都有效,并且无需将正则表达式的实现细节放入数据文件中。这样,您可以隐藏文本文件中匹配实际发生方式的实现细节,这种划分会产生强大的代码。

当然,如果您在文件中想要的是正则表达式列表,您显然不想使用Pattern.quote(),而是需要明确表达它期望用户输入需要是有效的Java正则表达式,并且可能会产生可怜的模式。

答案 1 :(得分:0)

文件中的

\) rparen和java字符串"...\\)..."中的反斜杠必须加倍以表示反斜杠。然后不需要quote,它也会更加间接。