Регулярное выражение для разбиения строки с использованием пробела, когда оно не заключено в одинарные или двойные кавычки - PullRequest
101 голосов
/ 14 декабря 2008

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

Пример ввода:

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' сохраняют пробел между словами.

Ответы [ 13 ]

223 голосов
/ 14 декабря 2008

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

[^\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());
} 
12 голосов
/ 14 декабря 2008

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

ОБНОВЛЕНИЕ : Пример регулярного выражения для обработки строк в одинарных и двойных кавычках. Ссылка: Как разделить строку, кроме случаев, когда она заключена в кавычки?

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

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

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

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

5 голосов
/ 14 декабря 2008

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

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

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

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

3 голосов
/ 12 декабря 2013

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

("[^"]*"|'[^']*'|[\S]+)+
2 голосов
/ 15 сентября 2010
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s

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

1 голос
/ 23 ноября 2017

Если вы используете 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);

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

Вывод:

This
is
a
string
that
will be
highlighted
when
your
regular expression 
matches
something random
1 голос
/ 27 мая 2014

Подход Яна великолепен, но вот еще один для протокола.

Если вы действительно хотите разделить, как указано в заголовке, сохраняя кавычки в "will be" и 'regular expression', то вы можете использовать этот метод, который прямо из соответствует (или заменяет) шаблон, кроме ситуации s1, s2, s3 и т. д.

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

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

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

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

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
1 голос
/ 22 февраля 2013

Мне понравился подход Маркуса, однако я изменил его, чтобы разрешить текст рядом с кавычками и поддерживать символы "и" в кавычках. Например, мне нужно было 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 голос
/ 14 декабря 2008

String.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."
1 голос
/ 14 декабря 2008

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

Причина в том, что вы можете разделить его на пробелы до и после "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"
...