You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中抗ReDoS的逗号前后去空格正则优化方案问询

优化Java正则:消除逗号前后空格时的回溯问题

我在Java中使用正则匹配「零个或多个空白字符+逗号+零个或多个空白字符」,用于清理字符串,初始代码如下:

String new1 = test1.replaceAll("\\s*,\\s*", ",");

回溯问题分析

当字符串中没有逗号时,这个正则会触发大量回溯。以字符串String s = "Hello World";(单词间4个空格,无逗号)为例,匹配过程如下:

  • 第一步:第一个\s*匹配"Hello"后的4个空格;
  • 第二步:尝试匹配逗号失败;
  • 第三步:回溯到第一个\s*,改为匹配3个空格后再次尝试匹配逗号,依然失败;
  • 第四步:重复上述回溯过程,直到穷尽所有可能,最终判定匹配失败。

尝试的优化方案

为了避免回溯,我尝试了两种优化方式:

  1. 原子组方案:
String new1 = test1.replaceAll("(?>\\s*),(?>\\s*)", ",");
  1. 占有量词方案:
String new1 = test1.replaceAll("\\s*+,\\s*+", ",");

基准测试结果

我用JMH编写了基准测试,测试字符串为"hello"和"world"之间包含50000个空格且无逗号的情况,测试结果如下:

Benchmark                    Mode  Cnt  Score   Error  Units
RegexBench.atomicGroup      thrpt       0.387          ops/s
RegexBench.replaceAll       thrpt       0.162          ops/s
RegexBench.replaceExtended  thrpt       0.248          ops/s

从结果来看,原子组方案的吞吐量约为原生方案的2倍,但整体性能仍未达到理想状态。

困惑点

通过调试工具分析,\s*,\s*仅需86步即可判定无匹配,而原子组模式需要172步,理论上原生方案应该更快,但Java中的实际测试结果却与此相反,这让我感到困惑。

基准测试代码

@State(Scope.Thread)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 1)
@Measurement(iterations = 1)
@Fork(1)
public class RegexBench {

    final static String s1 = divideTwoWordsByNumberOfSpaces("hello", "world", 50000);

    public static void main(final String[] args) throws IOException {
        org.openjdk.jmh.Main.main(args);
    }

    static String divideTwoWordsByNumberOfSpaces(final String word1, final String word2, final int numberOfSpaces) {
        final StringBuilder sb = new StringBuilder();
        sb.append(word1);
        for (int i = 0; i < numberOfSpaces; i++) {
            sb.append(" ");
        }
        sb.append(word2);
        return sb.toString();
    }

    @Benchmark
    public void replaceAll(final Blackhole blackhole) {
        String new1 = s1.replaceAll("\\s*,\\s*", ",");
    }

    @Benchmark
    public void replaceExtended(final Blackhole blackhole) {
        String new1 = s1.replaceAll("\\s*+,\\s*+", ",");
    }

    @Benchmark
    public void atomicGroup(final Blackhole blackhole){
        String new1 = s1.replaceAll("(?>\\s*),(?>\\s*)", ",");
    }
}

依赖配置(pom.xml)

<!-- JMH核心依赖 -->
<dependency>
  <groupId>org.openjdk.jmh</groupId>
  <artifactId>jmh-core</artifactId>
  <version>1.36</version>
</dependency>

<!-- JMH注解处理器 -->
<dependency>
  <groupId>org.openjdk.jmh</groupId>
  <artifactId>jmh-generator-annprocess</artifactId>
  <version>1.36</version>
</dependency>

内容的提问来源于stack exchange,提问作者SkogensKonung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 10:17:51