2009-07-08 13 views
3

Кажется простым, но я не могу заставить его работать.Regex заменить часть строки пробелами

У меня есть строка, которая выглядит как «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

Как бы вы сказали разницу между «А» и другими? «Всегда ли« А »приходит после« Д »? – BryanH

+1

Как вы определяете разницу между последним «D» и первым «A»? Является ли группа «А» гарантией того, что она не будет символом типа «D»? –

+0

Curtis Tasker правильный, сначала A после NNDDDD всегда N, остальное - это что угодно. – 2009-07-09 14:15:41

ответ

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

Возможно, вы найдете this post. Конечно, приведенный выше код предполагает, что в строке будет по крайней мере одна цифра - все символы, следующие за последней цифрой, преобразуются в пробелы. Если цифр нет, каждый символ преобразуется в пробел.

+0

Я думаю, что вы правы. Я был рефакторинг старого кода, который имеет несколько циклов и indexOf()/substring(), и я думал, что это можно сделать с помощью простого регулярного выражения. Даже не подумал об очистке старой логики. Я думаю, что ваш подход был бы наиболее эффективным для этой задачи. Спасибо за размышление вне коробки, то есть мои первоначальные требования. – 2009-07-09 14:30:44

+0

Ваш код предполагает, что часть AAA будет не цифрами. Это противоречит описанию проблемы, в котором говорится, что A будет «чем угодно», которое может содержать цифры. –

+0

Хорошо, тогда решение может быть слегка адаптировано для определения точки, где цифра сопровождается не цифрой. Это все еще проще, чем при использовании регулярных выражений, где они не нужны. –

1

Что вы подразумеваете под nondigit vs anything?

[^a-zA-Z0-9]
соответствует все, что не буква или цифра.

Вы хотите заменить все, что соответствует приведенному выше регулярному выражению, с пробелом.

Это то, о чем вы говорили?

+0

Вы не имеете в виду/[^ a-zA-Z0-9]// g? – BryanH

+0

, который удалит совпадения «ничего», я просто хотел выкинуть регулярное выражение, которое на самом деле соответствует «чему-либо». Я вычеркиваю черты, чтобы прояснить ситуацию. Благодарю. –

+0

«ничего» означает что угодно, т. Е. Буквы, цифры, пробелы. Я хочу заменить каждое вхождение пробелом. Например, «AA12345d4%» будет заменен на «AA12345» (четыре пробела в конце) – 2009-07-09 14:24:45

1

Вы хотите использовать положительный внешний вид, чтобы соответствовать N и D, а затем использовать нормальное соответствие для A.

Не уверен в положительном взгляде сзади грамматики в Java, но некоторые статьи на Java regex with look behind

+0

Я как раз собирался опубликовать это ... честно! Не знаю, разрешено ли вам иметь переменную длину за шаблоном, хотя, например, (? <= \ D +) –

+0

Не уверен, о регулярном выражении Java: я прочитал несколько статей, говорящих о pos/neg, заглядывая вперед/за ограничения в трех основных вариантах двигателей регулярных выражений и в главном отрыве у меня было регулярное регулярное выражение .Net, но иногда просто потому, что оно может не означать, что вы должны. –

+0

Вот хорошее описание поддержки различных движков для поиска позади: http://www.regular-expressions.info/lookaround.html#limitbehind – laz

3

Учитывая ваше описание, я предполагаю, что после NNDDDDD части, первая A на самом деле будет N, а не A , поскольку в противном случае между участками DDDDD и AAAA нет сплошной границы.

Итак, ваша строка на самом деле выглядит как NNDDDDDNAAA, и вы хотите заменить часть NAAA пробелами. Учитывая это, регулярное выражение может быть переписано как таковое: (\\D+\\d+)(\\D.+)

Положительный lookbehind в Java требует шаблона фиксированной длины; Вы не можете использовать шаблоны + или *. Вместо этого вы можете использовать фигурные скобки и указать максимальную длину. Например, вы можете использовать {1,9} вместо каждого +, и он будет соответствовать между 1 и 9 символами: (?<=\\D{1,9}\\d{1,9})(\\D.+)

Единственной проблемой здесь вы соответствием последовательности Naaa в одной игре, так что использование "NNNDDDDNAAA".replaceAll("(?<=\\D{1,9}\\d{1,9})(\\D.+)", " ") приведет при замене всей последовательности NAAA на единое пространство, а не на несколько пробелов.

Вы можете взять начальный разделитель совпадения и длину строки и использовать это, чтобы добавить правильное количество пробелов, но я не вижу смысла. Я думаю, вам лучше с вашим оригинальным решением; Его просто и легко следовать.

Если вы ищете небольшую дополнительную скорость, вы можете скомпилировать свой шаблон вне функции и использовать 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)); 
} 

} 

Update: Я написал быстрое итеративное решение, и провел несколько случайных данных через оба. Итеративное решение примерно в 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; 
} 
Смежные вопросы