【问题标题】:Am I reinventing the wheel with this token replacing code?我是否用这个令牌替换代码重新发明轮子?
【发布时间】:2018-08-25 08:53:31
【问题描述】:

我有一行包含嵌套标记的文本(如{}),我希望转换嵌套在特定深度的某些子字符串。

例如,在深度 1 处将单词 moo 大写:

moo [moo [moo moo]] moo ->

哞[哞[哞​​[哞​​]]哞

实现者:

replaceTokens(input, 1, "[", "]", "moo", String::toUpperCase);

或现实世界的示例,提供尚未使用颜色序列青色着色的“--options”:

@|blue --ignoreLog|@ 有效,但 --ignoreOutput 使一切静音。 ->

@|blue --ignoreLog|@ 有效,但@|cyan --ignoreOutput|@ 使一切静音。

实现者:

replaceTokens(input, 0, "@|", "|@", "--\\w*", s -> format("@|cyan %s|@", s));

我已经实现了这个逻辑,虽然我感觉很好(可能除了性能),但我也觉得我重新发明了轮子。以下是我的实现方式:

set currentPos to zero

while (input line not fully consumed) {
    take the remaining line

    if the open token is matched, add to output, increase counter and advance pos accordingly
    else if the close token is matched, add to output, decrease counter and advance pos accordingly
    else if the counter matches provided depth and given regex matches, invoke replacer function and advance pos accordingly
    else just record the next character and advance pos by 1
}

这是实际的实现:

public static String replaceNestedTokens(String lineWithTokens, int nestingDepth, String tokenOpen, String tokenClose, String tokenRegexToReplace, Function<String, String> tokenReplacer) {
    final Pattern startsWithOpen = compile(quote(tokenOpen));
    final Pattern startsWithClose = compile(quote(tokenClose));
    final Pattern startsWithTokenToReplace = compile(format("(?<token>%s)", tokenRegexToReplace));

    final StringBuilder lineWithTokensReplaced = new StringBuilder();

    int countOpenTokens = 0;
    int pos = 0;

    while (pos < lineWithTokens.length()) {
        final String remainingLine = lineWithTokens.substring(pos);

        if (startsWithOpen.matcher(remainingLine).lookingAt()) {
            countOpenTokens++;
            lineWithTokensReplaced.append(tokenOpen);
            pos += tokenOpen.length();
        } else if (startsWithClose.matcher(remainingLine).lookingAt()) {
            countOpenTokens--;
            lineWithTokensReplaced.append(tokenClose);
            pos += tokenClose.length();
        } else if (countOpenTokens == nestingDepth) {
            Matcher startsWithTokenMatcher = startsWithTokenToReplace.matcher(remainingLine);
            if (startsWithTokenMatcher.lookingAt()) {
                String matchedToken = startsWithTokenMatcher.group("token");
                lineWithTokensReplaced.append(tokenReplacer.apply(matchedToken));
                pos += matchedToken.length();
            } else {
                lineWithTokensReplaced.append(lineWithTokens.charAt(pos++));
            }
        } else {
            lineWithTokensReplaced.append(lineWithTokens.charAt(pos++));
        }
        assumeTrue(countOpenTokens >= 0, "Unbalanced token sets: closed token without open token\n\t" + lineWithTokens);
    }
    assumeTrue(countOpenTokens == 0, "Unbalanced token sets: open token without closed token\n\t" + lineWithTokens);
    return lineWithTokensReplaced.toString();
}

我无法使用像thisthis(或扫描仪)解决方案这样的正则表达式,但我觉得我正在重新发明轮子,并且可以使用(vanilla Java)解决这个问题-box 类的代码更少。此外,我很确定这是所有内联模式/匹配器实例和子字符串的性能噩梦。

建议?

【问题讨论】:

    标签: java regex java.util.scanner tokenize


    【解决方案1】:

    您可以使用像 ANTLR 这样的解析器来创建语法来描述您的语言或语法。然后使用侦听器或访问者来解释令牌。

    语法示例如下(我可以从您的代码中推断出):

    grammar Expr;       
    prog:   (expr NEWLINE)* ;
    expr:   id '[' expr ']'
        |   '@|' expr '|@'
        |   '--ignoreLog' expr
        |   '--ignoreOutput' expr
        |   string
        ;
    string: [a-zA-Z0-9];
    NEWLINE : [\r\n]+ ;
    

    【讨论】:

    • 答案不够详细。请举例。
    猜你喜欢
    • 2016-10-03
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    • 2013-05-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 2012-04-07
    相关资源
    最近更新 更多