是否可以使用“$1”作为另一个方法的参数并将返回的字符串放在它在 .replaceAll 中的位置上?

Is it possible to use "$1" as argument for another method and place returned String on it's place in .replaceAll?

我正在编写标签->HTML 解析器 - 用户输入一些带有自定义标签的文本(介于 BB 和 Markdown 之间的东西,例如 [b][#RRGGBB]),然后它正在解析到 HTML - 因此 [#FF0000]red text[/#] 变成 <span style='color:#FF0000;'>red text</strong>,然后以 HTML 样式放置在 JTextPane 中。

所有用户标签和 HTML 存储在 HashMap 中并像这样解析:

public static String parseBBToHTML(String text) {
    String html = text;

    Map<String,String> bbMap = new HashMap<String , String>();

    bbMap.put("\[b\]", "<strong>");
    bbMap.put("\[/b\]", "</strong>");
    ...
    bbMap.put("\[color=(.+?)\]", "<span style='color:;'>");
    bbMap.put("\[/color\]", "</span>");

    for (Map.Entry entry: bbMap.entrySet()) {
        html = html.replaceAll(entry.getKey().toString(), entry.getValue().toString());
    }

    return html;
}

然后返回值用于 JTextPane 中的 .setText() 以及其他方法的动画效果,只需一个接一个地键入字母并在它们之间暂停。

一切正常,但现在我在想一件事:

我在用空 HTML 标签模仿打字动画时的暂停,如下所示:<!!!!!!!!!!>

所以 10 个“!”它们之间有 20 毫秒的停顿,给我 200 毫秒的停顿。 我正在用这个方法形成 "pause tags"(它的参数是 int,但你会明白,为什么我现在使用 String):

public static String pause(String ms){
    String pausedText = "";
    int time = Integer.parseInt(ms);
    for (int i = 0; i < (time/animationSpeed); i++) {
        pausedText = pausedText + "!";
    }       
    return "<"+pausedText+">";
}

我想使用像 [!2000] 这样的标签来暂停 2000 毫秒。所以我只是像这样输入我的解析器字符串:

bbMap.put("\[\!(.+?)\]", Dialogue.pause(""));

...它没有用。它在方法 Dialogue.pause literally 字符串“$1”中给出,而不是 </code> 作为解析值。</p> <p>如何使用 <code> 作为参数来形成 "pause tag" 并将其放在文本中?

在Java中$1在捕获命令之外没有特殊意义,不是全局变量。您需要解析字符串并将组捕获到局部变量中以便将来使用它。

看这里:Java Regex Replace with Capturing Group

这意味着您当前使用 bbMap 的方法不适用于这些替换。

您可以这样做:运行 首先修改您的 bbMap。然后运行第二组修改bbMap2,首先捕获组,计算长度,根据长度创建你想要的字符串,然后替换。

如果我没有记错,用你的其他代码调用 bbMap.put("\[\!(.+?)\]", Dialogue.pause("")); 你想实现这样的事情:

  • 正则表达式找到组 1 的匹配项
  • Dialogue.pause("$1") 使用第 1 组的内容(应该是一个数字)调用

问题是它不能那样工作,因此您可以考虑重构您的代码。与其遍历可能的标签(即 bbMap 中的条目)并在输入中查找它们,不如遍历输入,获取开始和结束标签并在地图中进行查找。

你可以用正则表达式做到这一点,但请注意,这与 HTML 有同样的问题:它不适合正则表达式,除非你真的知道数据的结构等等。

所以您可能想要做的是使用表达式查找所有标签,获取标签 name/content 然后进行替换。它可能看起来像这样:

Pattern tagPattern = Pattern.compile("\[(/?)([^\]]*)\]");
Matcher tagMatcher = tagPattern.matcher( input );    

StringBuffer output = new StringBuffer();
while( tagMatcher.find() ) {
  String closeMarker = tagMatcher.group( 1 );
  String tagContent = tagMatcher.group( 2 );

  //use the tagContent to do the lookup in the map and create the replacement string
  String replacement = ...;

  tagMatcher.appendReplacement( output, replacement );
}
tagMatcher.appendTail( output ); 

字符串只是一个单一的静态值。你想要的是一个从另一个字符串动态计算的字符串。

您需要将地图更改为:

Map<String, String> bbMap = new HashMap<String, String>();

对此:

Map<String, UnaryOperator<String>> bbMap = new HashMap<>();

A UnaryOperator<String> 是一个接受字符串参数和 returns 字符串值的函数。因此,您可以像这样填充地图:

bbMap.put("\[b\]", s -> "<strong>");
bbMap.put("\[/b\]", s -> "</strong>");
//...
bbMap.put("\[color=(.+?)\]", s -> "<span style='color:" + s + ";'>");
bbMap.put("\[/color\]", s -> "</span>");

bbMap.put("\[\!(.+?)\]", s -> Dialogue.pause(s));

for (Map.Entry entry : bbMap.entrySet()) {
    StringBuffer buffer = new StringBuffer(html.length());

    Matcher matcher =
        Pattern.compile(entry.getKey().toString()).matcher(html);
    while (matcher.find()) {
        String match =
            (matcher.groupCount() > 0 ? matcher.group(1) : null);
        String replacement = entry.getValue().apply(match);
        matcher.appendReplacement(buffer,
            Matcher.quoteReplacement(replacement));
    }
    matcher.appendTail(buffer);

    html = buffer.toString();
}