当没有单引号或双引号时,使用空格分割字符串的正则表达式

我对正则表达式不熟悉,希望您能帮助我。我正在尝试组合一个表达式,它将使用所有没有被单引号或双引号包围的空格来拆分示例字符串。我的最后一次尝试看起来像这样: (?!")和不是很工作。引用前的空格分开了。

示例输入:

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'保留了单词之间的空格。

103064 次浏览

搜索字符串,抓取每个部分,或者分割它,可能会更容易。

原因是,您可以在 "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."

我有理由相信,仅仅使用正则表达式是不可能做到这一点的。检查某些内容是否包含在其他标记中是一个解析操作。这似乎与尝试使用正则表达式解析 XML 是相同的问题——无法正确解析。你可以通过重复应用一个非贪婪的、非全局的匹配引用字符串的正则表达式来获得你想要的结果,然后一旦你找不到其他的东西,就把它分割到空格中... ... 这有很多问题,包括跟踪所有子字符串的原始顺序。最好的办法是编写一个非常简单的函数,迭代字符串并提取所需的标记。

关于 StackOverflow,有几个问题可以在不同的上下文中使用正则表达式来回答同样的问题,例如:

UPDATE : 处理单引号和双引号字符串的示例正则表达式。除了内部引号之外,我怎样才能在一个字符串上进行分割?

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.htmhttp://gskinner.com/RegExr/

我不明白为什么其他人都提出这么复杂的正则表达式或这么长的代码。本质上,您希望从字符串中获取两种东西: 不是空格或引号的字符序列,以及以引号开始和结束的字符序列(中间没有引号) ,用于两种引号。您可以很容易地用这个正则表达式来匹配这些内容:

[^\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());
}
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s

这将匹配未被双引号包围的空格。 我必须使用 min,max {0,99999} ,因为 Java 不支持 * and + in lookbehind。

我喜欢马库斯的方法,但是,我修改了它,以便我可以允许文本附近的引号,并支持“和’引号字符。例如,我需要 a = “ some value”来避免将其拆分为[ a = ,“ some value”]。

(?<!\\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|([^\s"']+)
  • 允许在带引号的字符串中使用转义引号
  • 避免重复单引号和双引号的模式; 这也简化了在需要时添加更多引号(以牺牲另一个捕获组为代价)

Jan Goyvaerts 的正则表达式是我目前为止找到的最好的解决方案,但是它也创建了空匹配(null) ,这在他的程序中被排除了。这些空匹配也出现在正则表达式测试程序中(例如 rubular.com)。 如果你反过来搜索(首先查找被引用的部分,然后查找空格隔开的单词) ,那么你可以用以下方法一次性完成:

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

简的方法很棒,但这里还有一个值得记录的方法。

如果你真的想要像标题中提到的那样分割,在 "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]);
}
}

如果使用 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

下面返回一个参数数组。参数是分隔在空格上的变量“命令”,除非包含在单引号或双引号中。然后修改匹配项以删除单引号和双引号。

using System.Text.RegularExpressions;


var args = Regex.Matches(command, "[^\\s\"']+|\"([^\"]*)\"|'([^']*)'").Cast<Match>
().Select(iMatch => iMatch.Value.Replace("\"", "").Replace("'", "")).ToArray();

使用 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个字符和右边的所有字符既不是单引号也不是双引号时,在空格处拆分

改编自 原帖 (只处理双引号)

当你看到这样的图案:

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"]

此正则表达式只有在后跟偶数双引号时才匹配空格。