Регулярное выражение для разделения строки с использованием пробела, когда оно не заключено в одинарные или двойные кавычки
Я новичок в регулярных выражениях и был бы признателен за вашу помощь. Я пытаюсь собрать выражение, которое разделит пример строки, используя все пробелы, которые не заключены в одинарные или двойные кавычки. Моя последняя попытка выглядит так: (?!")
и не совсем работает. Это расщепляется на пространство перед цитатой.
Пример ввода:
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'
сохранить пространство между словами.
16 ответов
Я не понимаю, почему все остальные предлагают такие сложные регулярные выражения или такой длинный код. По сути, вы хотите получить два вида вещей из вашей строки: последовательности символов, которые не являются пробелами или кавычками, и последовательности символов, которые начинаются и заканчиваются кавычками, без кавычек между ними, для двух типов кавычек. Вы можете легко сопоставить эти вещи с этим регулярным выражением:
[^\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());
}
В Stackru есть несколько вопросов, которые охватывают этот же вопрос в различных контекстах с использованием регулярных выражений. Например:
ОБНОВЛЕНИЕ: пример регулярного выражения для обработки строк в одинарных и двойных кавычках. Ссылка: Как разделить строку, кроме случаев, когда она заключена в кавычки?
m/('.*?'|".*?"|\S+)/g
Протестировал это с быстрым фрагментом Perl, и результат был как показано ниже. Также работает для пустых строк или строк только для пробелов, если они находятся между кавычками (не уверен, если это желательно или нет).
This
is
a
string
that
"will be"
highlighted
when
your
'regular expression'
matches
something.
Обратите внимание, что это включает сами символы кавычек в совпадающие значения, хотя вы можете удалить их с помощью замены строки или изменить регулярное выражение, чтобы они не включались. Я пока оставлю это как упражнение для читателя или другого автора, так как 2 часа ночи уже слишком поздно, чтобы больше возиться с регулярными выражениями;)
Если вы хотите разрешить экранированные кавычки внутри строки, вы можете использовать что-то вроде этого:
(?:(['"])(.*?)(?<!\\)(?>\\\\)*\1|([^\s]+))
Строки в кавычках будут группой 2, одиночные слова без кавычек будут группой 3.
Вы можете попробовать его на различных строках здесь: http://www.fileformat.info/tool/regex.htm или http://gskinner.com/RegExr/
Регулярное выражение от Jan Goyvaerts - лучшее решение, которое я нашел до сих пор, но оно также создает пустые (нулевые) совпадения, которые он исключает из своей программы. Эти пустые совпадения также появляются от тестеров регулярных выражений (например, rubular.com). Если вы включите поиск по кругу (сначала ищите процитированные части, а не слова, разделенные пробелами), то вы можете сделать это за один раз:
("[^"]*"|'[^']*'|[\S]+)+
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s
Это будет соответствовать пробелам, не окруженным двойными кавычками. Я должен использовать min,max {0,99999}, потому что Java не поддерживает * и + в вид сзади.
Надеюсь, что пара полезных поправок на принятый ответ Яна:
(['"])((?:\\\1|.)+?)\1|([^\s"']+)
- Позволяет экранировать кавычки внутри строк в кавычках
- Избегает повторения шаблона для одинарных и двойных кавычек; это также упрощает добавление большего количества символов цитирования, если это необходимо (за счет еще одной группы захвата)
Когда вы сталкиваетесь с этим шаблоном, как это:
String str = "2022-11-10 08:35:00,470 RAV=REQ YIP=02.8.5.1 CMID=caonaustr CMN=\"Some Value Pyt Ltd\"";
//this helped
String[] str1= str.split("\\s(?=(([^\"]*\"){2})*[^\"]*$)\\s*");
System.out.println("Value of split string is "+ Arrays.toString(str1));
Это приводит к:[2022-11-10, 08:35:00,470, PLV=REQ, YIP=02.8.5.1, CMID=caonaustr, CMN="Some Value Pyt Ltd"]
Это регулярное выражение соответствует пробелам, ТОЛЬКО если за ним следует четное количество двойных кавычек.
Если вы используете 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
Мне понравился подход Маркуса, однако я изменил его так, чтобы я мог разрешить текст рядом с кавычками и поддерживать символы "и" в кавычках. Например, мне нужно было 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"
Вероятно, будет легче искать строку, захватывая каждую часть, а не разбивать ее.
Причина в том, что вы можете разбить его на пробелы до и после "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"
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-й однострочный с использованием String.split()
String s = "This is a string that \"will be\" highlighted when your 'regular expression' matches something.";
String[] split = s.split( "(?<!(\"|').{0,255}) | (?!.*\\1.*)" );
[This, is, a, string, that, "will be", highlighted, when, your, 'regular expression', matches, something.]
не разделять пробел, если пробел заключен в одинарные или двойные кавычки,
разделенный на пробел, когда 255 символов слева и все символы справа от пробела не являются ни одинарными, ни двойными кавычками
адаптировано из оригинального сообщения (обрабатывает только двойные кавычки)
Подход Яна великолепен, но вот еще один для протокола.
Если вы действительно хотите разделить, как указано в заголовке, оставьте цитаты в "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
Вы также можете попробовать это:
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]);
}
}
Я вполне уверен, что это невозможно при использовании только регулярных выражений. Проверка, содержится ли что-то внутри какого-либо другого тега, является операцией синтаксического анализа. Это похоже на ту же проблему, что и попытка синтаксического анализа XML с помощью регулярного выражения - это не может быть сделано правильно. Вы можете получить желаемый результат, многократно применяя не жадное неглобальное регулярное выражение, которое совпадает с указанными строками в кавычках, а затем, если вы не можете найти ничего другого, разбейте его на пробелы..., которые имеют ряд проблемы, в том числе отслеживание исходного порядка всех подстрок. Лучше всего написать просто простую функцию, которая перебирает строку и извлекает нужные вам токены.
Следующее возвращает массив аргументов. Аргументы - это переменная command, разделенная пробелами, если они не заключены в одинарные или двойные кавычки. Затем совпадения модифицируются, чтобы удалить одинарные и двойные кавычки.
using System.Text.RegularExpressions;
var args = Regex.Matches(command, "[^\\s\"']+|\"([^\"]*)\"|'([^']*)'").Cast<Match>
().Select(iMatch => iMatch.Value.Replace("\"", "").Replace("'", "")).ToArray();