使用正则表达式将字符串的一部分替换为空格

3

看起来很简单,但我却做不到。

我有一个字符串,长这样:'NNDDDDDAAAA',其中 'N' 表示非数字,'D' 表示数字,'A' 表示任意字符。我需要将每个 'A' 替换为空格。输入字符串中的 'N'、'D' 和 'A' 的数量总是不同的。

我知道如何用两个表达式来做到这一点。我可以将一个字符串分成两部分,然后用空格替换第二组中的所有内容。就像这样:

    Pattern pattern = Pattern.compile("(\\D+\\d+)(.+)");
    Matcher matcher = pattern.matcher(input);
    if (matcher.matches()) {
        return matcher.group(1) + matcher.group(2).replaceAll(".", " ");
    }

但我在想,是否有可能用一个正则表达式实现这个功能。

2
你如何区分'A'和其他字母?'A'总是在'D'之后吗? - BryanH
1
你如何区分最后一个“D”和第一个“A”之间的差别?一组“A”是否保证不是“D”类型字符? - Jason Musgrove
Curtis Tasker 是正确的,NNDDDD 后面的第一个 A 总是 N,其余可以是任何字符。 - user135273
4个回答

3
考虑到您的描述,我假设在 "NNDDDDD" 的部分之后,第一个 "A" 实际上会是一个 "N" 而不是 "A",否则 "DDDDD" 和 "AAAA" 之间就没有明确的边界。因此,您的字符串实际上看起来像是 "NNDDDDDNAAA",您想用空格替换 "NAAA" 部分。基于此,正则表达式可以重写为:"(\\D+\\d+)(\\D.+)"。
Java 中的正向回顾需要一个固定长度的模式;您不能使用 "+" 或 "*" 模式。相反,您可以使用花括号并指定最大长度。例如,您可以使用 "{1,9}" 来代替每个 "+",它将匹配 1 到 9 个字符:"(?<=\\D{1,9}\\d{1,9})(\\D.+)"
这里唯一的问题是,您将 NAAA 序列作为单个匹配项进行匹配,因此使用 "NNNDDDDNAAA".replaceAll("(?<=\\D{1,9}\\d{1,9})(\\D.+)", " ") 将导致用单个空格替换整个 "NAAA" 序列,而不是多个空格。
您可以采取匹配的开始定界符和字符串长度,并使用它来附加正确数量的空格,但我认为这没有意义。我认为您最好使用原始解决方案;它简单易懂。
如果您想要更快的速度,可以在函数外编译您的 Pattern,并使用 StringBuilder 或 StringBuffer 来创建输出。如果您正在从所有这些 NNDDDDDAAAAA 元素中构建一个大字符串,请完全使用 StringBuilder 直到您完成附加。
class Test {

public static Pattern p = Pattern.compile("(\\D+\\d+)(\\D.+)");

public static StringBuffer replace( String input ) {
    StringBuffer output = new StringBuffer();
    Matcher m = Test.p.matcher(input);
    if( m.matches() )
        output.append( m.group(1) ).append( m.group(2).replaceAll("."," ") );

    return output;
}

public static void main( String[] args ) {
    String input = args[0];
    long startTime;

    StringBuffer tests = new StringBuffer();
    startTime = System.currentTimeMillis();
        for( int i = 0; i < 50; i++)
        {
            tests.append( "Input -> Output: '" );
            tests.append( input );
            tests.append( "' -> '" );
            tests.append( Test.replace( input ) );
            tests.append( "'\n" );
        }
    System.out.println( tests.toString() );
    System.out.println( "\n" + (System.currentTimeMillis()-startTime));
}

}

更新: 我写了一个快速迭代的解决方案,并对两种方法运行了一些随机数据。这个迭代解决方案大约快了4-5倍。

public static StringBuffer replace( String input )
{
    StringBuffer output = new StringBuffer();
    boolean second = false, third = false;
    for( int i = 0; i < input.length(); i++ )
    {
        if( !second && Character.isDigit(input.charAt(i)) )
            second = true;

        if( second && !third && Character.isLetter(input.charAt(i)) )
            third = true;

        if( second && third )
            output.append( ' ' );
        else
            output.append( input.charAt(i) );

    }

    return output;
}

1

你说的非数字与任何东西是什么意思?

[^a-zA-Z0-9]
匹配所有不是字母或数字的内容。

你需要用空格替换上述正则表达式匹配到的任何内容。

这就是你所说的内容吗?


难道您的意思不是/ [ ^ a-zA-Z0-9 ] / / g吗? - BryanH
这将删除与“anything”匹配的内容,我只是想提供实际匹配“anything”的正则表达式。我会去掉斜杠以澄清事情。谢谢。 - Robert Greiner
“anything” 意味着任何字符,包括字母、数字和空格。我想用一个空格替换每个出现的字符。例如,“AA12345d4%”将被替换为“AA12345 ”(末尾有四个空格)。 - user135273

1

您想使用正向后瞻来匹配N和D,然后对A使用普通匹配。

不确定Java中正向后瞻语法的用法,但可以参考Java regex with look behind上的一些文章。


我正要发布那个...真的!不知道你是否允许使用可变长度的后置模式,例如(?<=\D+)。 - Amal Sirisena
不确定关于Java正则表达式:我读过一些文章讨论了三种主要变体的正则表达式引擎中的正面/负面前后限制,我的主要收获是.Net正则表达式可以做好的事情,但有时候仅仅因为它可以并不意味着你应该这样做。 - Simeon Pilgrim
这里有一个关于各种引擎支持后顾限定的好描述:http://www.regular-expressions.info/lookaround.html#limitbehind - laz
不,通常情况下它们不允许可变宽度的反向引用。"(?<=\D+)"是被允许的,因为它等同于固定宽度的反向引用"(?<=\D)"。 - newacct
1
无论如何,即使向后查找起作用,也无法解决OP的问题,即用空格替换匹配组中的每个字符。没有替换字符串可以让您执行“用相同长度的空格字符串替换此内容”的操作。 - newacct

0

我知道你要求一个正则表达式,但是为什么你需要一个正则表达式呢?这个怎么样:

StringBuilder sb = new StringBuilder(inputString);
for (int i = sb.length() - 1; i >= 0; i--) {
    if (Character.isDigit(sb.charAt(i)))
        break;
    sb.setCharAt(i, ' ');
}
String output = sb.toString();

你可能会觉得这篇文章很有趣。当然,上面的代码假设字符串中至少有一个数字 - 最后一个数字后面的所有字符都会被转换为空格。如果没有数字,则每个字符都会被转换为空格。


我认为你是正确的。我正在重构一些旧代码,其中有多个循环和indexOf()/substring(),我认为可以用简单的正则表达式来完成。甚至没有考虑清理旧逻辑。我认为你的方法对于这个任务来说是最有效的。 感谢你超越了盒子,即我的初始要求。 - user135273
您的代码假定 AAA 部分不包含数字。这与问题描述相反,问题描述中指出 A 可以是“任何东西”,这可能包括数字。 - Curtis Tasker
那么,解决方案可以稍微调整一下,以定位数字后面跟着非数字的点。这仍然比在不必要的情况下使用正则表达式更简单。 - Vinay Sajip
是的,我不得不添加额外的逻辑来找到允许数字的点。仍然相当简单。 - user135273

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接