2008-12-14 7 views
93

Я новичок в регулярных выражениях и буду благодарен вам за помощь. Я пытаюсь собрать выражение, которое разделит строку примера, используя все пробелы, которые не окружены одинарными или двойными кавычками. Моя последняя попытка выглядит так: (?!") и не совсем работает. Это расщепление на пространстве перед цитатой.Регулярное выражение для разделения строки с использованием пробела, когда оно не окружено одинарными или двойными кавычками

Пример входных данных:

This is a string that "will be" highlighted when your 'regular expression' matches something. 

Желаемый выход:

This 
is 
a 
string 
that 
will be 
highlighted 
when 
your 
regular expression 
matches 
something. 

"will be" Обратите внимание, что и 'regular expression' сохранить пространство между словами.

+0

ли вы на самом деле, используя метод «разделения», или же цикл с методом «найти» на Matcher будет достаточно « – erickson 2008-12-14 05:31:46

+5

», и теперь у него две проблемы » – hop 2008-12-14 05:44:12

ответ

206

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

[^\s"']+|"([^"]*)"|'([^']*)' 

Я добавил, захватив группы, потому что вы не хотите, котировки в списке.

Этот код Java создает список, добавляя группу захвата, если он соответствует исключению кавычек, и добавляет общее соответствие регулярному выражению, если группа захвата не соответствует (сопоставлено несогласованное слово).

List<String> matchList = new ArrayList<String>(); 
Pattern regex = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); 
Matcher regexMatcher = regex.matcher(subjectString); 
while (regexMatcher.find()) { 
    if (regexMatcher.group(1) != null) { 
     // Add double-quoted string without the quotes 
     matchList.add(regexMatcher.group(1)); 
    } else if (regexMatcher.group(2) != null) { 
     // Add single-quoted string without the quotes 
     matchList.add(regexMatcher.group(2)); 
    } else { 
     // Add unquoted word 
     matchList.add(regexMatcher.group()); 
    } 
} 

Если вы не возражаете, имеющие котировки в возвращаемом списке, вы можете использовать гораздо более простой код:

List<String> matchList = new ArrayList<String>(); 
Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'"); 
Matcher regexMatcher = regex.matcher(subjectString); 
while (regexMatcher.find()) { 
    matchList.add(regexMatcher.group()); 
} 
1

Скорее всего, будет проще искать строку, захватывая каждую часть, а также разделять ее.

Причина в том, что вы можете разделить ее на пространства до и после "will be". Но я не могу придумать, как можно указать игнорирование пространства между разделом.

(не актуально Java)

string = "This is a string that \"will be\" highlighted when your 'regular expression' matches something."; 

regex = "\"(\\\"|(?!\\\").)+\"|[^ ]+"; // search for a quoted or non-spaced group 
final = new Array(); 

while (string.length > 0) { 
    string = string.trim(); 
    if (Regex(regex).test(string)) { 
     final.push(Regex(regex).match(string)[0]); 
     string = string.replace(regex, ""); // progress to next "word" 
    } 
} 

Кроме того, захватывая одиночные кавычки могут привести к проблемам:

"Foo's Bar 'n Grill" 

//=> 

"Foo" 
"s Bar " 
"n" 
"Grill" 
+0

Ваше решение не обрабатывает строки с одним кавычком, которые являются частью примера Карла. – 2008-12-14 13:08:50

1

String.split() не полезно здесь, потому что нет никакого способа отличить между пробелами внутри кавычек (не разделяются) и теми, которые находятся за пределами (split). Matcher.lookingAt(), вероятно, что вам нужно:

String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something."; 
str = str + " "; // add trailing space 
int len = str.length(); 
Matcher m = Pattern.compile("((\"[^\"]+?\")|('[^']+?')|([^\\s]+?))\\s++").matcher(str); 

for (int i = 0; i < len; i++) 
{ 
    m.region(i, len); 

    if (m.lookingAt()) 
    { 
     String s = m.group(1); 

     if ((s.startsWith("\"") && s.endsWith("\"")) || 
      (s.startsWith("'") && s.endsWith("'"))) 
     { 
      s = s.substring(1, s.length() - 1); 
     } 

     System.out.println(i + ": \"" + s + "\""); 
     i += (m.group(0).length() - 1); 
    } 
} 

, который производит следующий вывод:

0: "This" 
5: "is" 
8: "a" 
10: "string" 
17: "that" 
22: "will be" 
32: "highlighted" 
44: "when" 
49: "your" 
54: "regular expression" 
75: "matches" 
83: "something." 
0

Я достаточно уверен, что это не представляется возможным с помощью регулярных выражений в одиночку. Проверка того, что что-то содержится внутри какого-либо другого тега, - это операция синтаксического анализа. Это похоже на ту же проблему, что и попытка разобрать XML с помощью регулярного выражения - это невозможно сделать правильно. Вы можете получить желаемый результат, повторно применяя не-жадное, неглобальное регулярное выражение, которое соответствует цитируемым строкам, а затем, когда вы не можете найти что-либо еще, разделите его на пробелы ..., в котором есть несколько проблемы, в том числе отслеживание первоначального порядка всех подстрок. Лучше всего просто написать действительно простую функцию, которая выполняет итерацию по строке и вытаскивает нужные вам жетоны.

+0

Это возможно с помощью регулярного выражения, см. Некоторые примеры, к которым я привязался. это, и я видел несколько похожих вопросов о SO, которые обращаются к этому с помощью регулярных выражений. – Jay 2008-12-14 06:39:53

+1

Знайте, когда не использовать регулярное выражение, это более полезное знание, чтобы иметь возможность создавать (?: (['"]) (. *?) (? \\\\) * \ 1 | ([^ \ s] +)) – Rene 2008-12-14 15:43:28

11

В StackOverflow есть несколько вопросов, которые охватывают этот же вопрос в различных контекстах с использованием регулярных выражений. Например:

UPDATE: Sample регулярное выражение для обработки одиночных и двойных кавычках строки.Ref: How can I split on a string except when inside quotes?

m/('.*?'|".*?"|\S+)/g 

Испытано это с быстрым фрагмент кода Perl, и выход был воспроизведен ниже. Также работает для пустых строк или строк только в виде пробелов, если они находятся между кавычками (не уверен, что это желательно или нет).

This 
is 
a 
string 
that 
"will be" 
highlighted 
when 
your 
'regular expression' 
matches 
something. 

Обратите внимание, что это действительно включает в себя кавычки в соответствующих значениях, хотя вы можете удалить с помощью строки заменить или изменить регулярное выражение, чтобы не включать их. Я оставлю это как упражнение для читателя или другого плаката на данный момент, так как 2am слишком поздно, чтобы больше не возиться с регулярными выражениями;)

+0

Я думаю, что ваше регулярное выражение допускает несогласованные кавычки, например «будет» и «регулярные выражения». – 2008-12-14 07:05:44

+0

@ Zach - вы правы, он ... обновил его, чтобы исправить это на всякий случай – Jay 2008-12-14 07:36:15

4

Если вы хотите разрешить скрытые кавычки внутри строки, вы можете использовать что-то вроде этого:

(?:(['"])(.*?)(?<!\\)(?>\\\\)*\1|([^\s]+)) 

кавычки будут группой 2, одиночные слова без кавычек будут группа 3.

Вы можете попробовать его на разных строках здесь: http://www.fileformat.info/tool/regex.htm или http://gskinner.com/RegExr/

2
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s 

Это будет соответствовать пространства не в двойных кавычках. Мне нужно использовать min, max {0,99999}, потому что Java не поддерживает * и + в lookbehind.

1

Мне понравился подход Маркуса, однако я изменил его так, чтобы я мог разрешить текст рядом с кавычками, и поддерживал оба символа «и». Например, мне нужно было a = «некоторое значение», чтобы не разбить его на [ a =, "некоторое значение"].

(?<!\\G\\S{0,99999}[\"'].{0,99999})\\s|(?<=\\G\\S{0,99999}\".{0,99999}\"\\S{0,99999})\\s|(?<=\\G\\S{0,99999}'.{0,99999}'\\S{0,99999})\\s" 
1

Несколько надеюсь, полезные твики на принятый ответ Яна:

(['"])((?:\\\1|.)+?)\1|([^\s"']+) 
  • Позволяет спасся кавычки в кавычках
  • Избегает повторяя шаблон для одиночной и двойной кавычки; это также упрощает добавление дополнительных котировочных символов, если необходимо (за счет еще одной группы захвата)
3

Регулярное выражение из Jan Goyvaerts - лучшее решение, которое я нашел до сих пор, но создает также пустые (нулевые) совпадения, которые он исключает его программу. Эти пустые совпадения также отображаются в тестерах regex (например, rubular.com). Если включить поиски Arround (сначала искать процитированные части и чем пространство separed слов), то вы можете сделать это один раз: подход

("[^"]*"|'[^']*'|[\S]+)+ 
1

Яны является большим, но вот еще один для записи.

Если вы на самом деле хотели разделить, как указано в названии, держа котировки в "will be" и 'regular expression', то вы можете использовать этот метод, который прямо из Match (or replace) a pattern except in situations s1, s2, s3 etc

регулярное выражение:

'[^']*'|\"[^\"]*\"|() 

Два левых чередования соответствуют полному 'quoted strings' и "double-quoted strings". Мы проигнорируем эти матчи. Правая сторона сопоставляет и фиксирует пробелы в группе 1, и мы знаем, что они являются правильными пространствами, потому что они не соответствовали выражениям слева. Мы заменяем их SplitHere, затем разделяем на SplitHere. Опять же, это для истинного раздельного случая, где вы хотите "will be", а не will be.

Вот полная рабочая реализация (см. Результаты на online demo).

import java.util.*; 
import java.io.*; 
import java.util.regex.*; 
import java.util.List; 

class Program { 
public static void main (String[] args) throws java.lang.Exception { 

String subject = "This is a string that \"will be\" highlighted when your 'regular expression' matches something."; 
Pattern regex = Pattern.compile("\'[^']*'|\"[^\"]*\"|()"); 
Matcher m = regex.matcher(subject); 
StringBuffer b= new StringBuffer(); 
while (m.find()) { 
    if(m.group(1) != null) m.appendReplacement(b, "SplitHere"); 
    else m.appendReplacement(b, m.group(0)); 
} 
m.appendTail(b); 
String replaced = b.toString(); 
String[] splits = replaced.split("SplitHere"); 
for (String split : splits) System.out.println(split); 
} // end main 
} // end Program 
0

Вы также можете попробовать это:

String str = "This is a string that \"will be\" highlighted when your 'regular expression' matches something"; 
    String ss[] = str.split("\"|\'"); 
    for (int i = 0; i < ss.length; i++) { 
     if ((i % 2) == 0) {//even 
      String[] part1 = ss[i].split(" "); 
      for (String pp1 : part1) { 
       System.out.println("" + pp1); 
      } 
     } else {//odd 
      System.out.println("" + ss[i]); 
     } 
    } 
0

Если вы используете C#, вы можете использовать

string input= "This is a string that \"will be\" highlighted when your 'regular expression' matches <something random>"; 

List<string> list1 = 
       Regex.Matches(input, @"(?<match>\w+)|\""(?<match>[\w\s]*)""|'(?<match>[\w\s]*)'|<(?<match>[\w\s]*)>").Cast<Match>().Select(m => m.Groups["match"].Value).ToList(); 

foreach(var v in list1) 
    Console.WriteLine(v); 

Я специально добавил "| < ([\ ш \? s] *)> ", чтобы подчеркнуть, что вы можете указать любой символ для группировки фраз. . (В этом случае я использую <> группе

Выход:

This 
is 
a 
string 
that 
will be 
highlighted 
when 
your 
regular expression 
matches 
something random 

 Смежные вопросы

  • Нет связанных вопросов^_^