【问题标题】:Regex to replace part of the string with spaces正则表达式用空格替换部分字符串
【发布时间】:2009-07-08 22:13:43
【问题描述】:

这看起来很简单,但我无法让它工作。

我有一个看起来像“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(".", " ");
    }

但我想知道是否可以使用单个正则表达式。

【问题讨论】:

  • 你如何区分'A'和其他的? 'A' 总是在 'D' 之后吗?
  • 如何区分最后一个“D”和第一个“A”?这组“A”是否保证不是“D”类型的字符?
  • Curtis Tasker 是正确的,NNDDDD 之后的第一个 A 总是 N,剩下的就是任何东西。

标签: java regex


【解决方案1】:

根据您的描述,我假设在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;
}

【讨论】:

    【解决方案2】:

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

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

    您可能想用空格替换上述正则表达式匹配的任何内容。

    你说的是这个吗?

    【讨论】:

    • 你不是说 /[^a-zA-Z0-9]/ /g 吗?
    • 这将删除“任何东西”匹配,我只是想抛出真正匹配“任何东西”的正则表达式,我将删除斜线以清除问题。谢谢。
    • 'anything' 表示任何东西,即字母、数字、空格。我想用空格替换每个出现。例如,“AA12345d4 %”将替换为“AA12345”(末尾四个空格)
    【解决方案3】:

    您想使用正向查找来匹配 N 和 D,然后对 A 使用正常匹配。

    不确定Java语法背后的积极看法,但有一些关于Java regex with look behind的文章

    【讨论】:

    • 我正要发布那个......老实说!不知道是否允许您在模式后面进行可变长度查看,例如 (?
    • 不确定,关于 Java 正则表达式:我已经阅读了一些关于正则表达式引擎的三个主要变体中的 pos/neg 前瞻/后向限制的文章,我得到的主要收获是.Net 正则表达式可以做的很好,但有时仅仅因为它可以并不意味着你应该这样做。
    • 这里有一个很好的描述各种引擎对后退的支持:regular-expressions.info/lookaround.html#limitbehind
    • 不,一般情况下它们不允许变宽向后看。 "(?
    • 而且在任何情况下,即使向后看也不能解决OP的问题,即用空格替换匹配组中的每个字符。没有替换字符串可以让您执行“用相同长度的空格字符串替换它”。
    【解决方案4】:

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

    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();
    

    您可能会觉得this post 很有趣。当然,上面的代码假定字符串中至少有一个数字——最后一个数字后面的所有字符都转换为空格。如果没有数字,则每个字符都转换为空格。

    【讨论】:

    • 我认为你是对的。我正在重构一些具有多个循环和 indexOf()/substring() 的旧代码,我认为可以使用简单的正则表达式来完成。甚至没有考虑清理旧的逻辑。我认为您的方法对于这项任务来说是最有效的。感谢您跳出框框思考,即我的初始要求。
    • 您的代码假定 AAA 部分是非数字的。这与问题描述相反,它说 A 将是“任何东西”,可能包括数字。
    • 那么,可以稍微调整解决方案以定位数字后跟非数字的点。它最终仍然比在不需要的地方使用正则表达式更简单。
    • 是的,我必须添加额外的逻辑来找到允许数字的点。还是很简单的
    猜你喜欢
    • 2017-11-13
    • 2017-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    相关资源
    最近更新 更多