126

我是正则表达式的新手,非常感谢您的帮助。我正在尝试组合一个表达式,该表达式将使用所有未被单引号或双引号包围的空格来拆分示例字符串。我的最后一次尝试看起来像这样:(?!")并且不太有效。它在报价前的空间上分裂。

示例输入:

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

4

15 回答 15

271

我不明白为什么所有其他人都提出如此复杂的正则表达式或如此长的代码。本质上,您想从字符串中获取两种内容:不是空格或引号的字符序列,以及以引号开头和结尾的字符序列,两者之间没有引号,用于两种引号。您可以使用此正则表达式轻松匹配这些内容:

[^\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());
} 
于 2008-12-14T13:07:29.240 回答
18

StackOverflow 上有几个问题使用正则表达式在各种上下文中涵盖了相同的问题。例如:

更新:用于处理单引号和双引号字符串的示例正则表达式。参考:我如何在字符串上拆分,除非在引号内?

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

使用快速 Perl 片段对此进行了测试,输出如下所示。如果它们在引号之间(不确定是否需要),也适用于空字符串或仅限空格的字符串。

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

请注意,这确实在匹配值中包含引号字符本身,尽管您可以使用字符串替换将其删除,或者修改正则表达式以不包含它们。我暂时把它作为练习留给读者或另一张海报,因为凌晨 2 点太晚了,不能再弄乱正则表达式了;)

于 2008-12-14T06:34:05.820 回答
6

如果你想在字符串中允许转义引号,你可以使用这样的东西:

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

带引号的字符串将是第 2 组,单个未引用的单词将是第 3 组。

您可以在此处尝试各种字符串:http ://www.fileformat.info/tool/regex.htm或http://gskinner.com/RegExr/

于 2008-12-14T06:45:11.790 回答
3

Jan Goyvaerts 的正则表达式是我迄今为止找到的最佳解决方案,但也创建了空(null)匹配,他在他的程序中排除了这些匹配。这些空匹配也出现在正则表达式测试器中(例如 rubular.com)。如果您将搜索转为 arround(首先查找引用的部分,然后查找空格分隔的单词),那么您可以使用以下方法进行一次:

("[^"]*"|'[^']*'|[\S]+)+
于 2013-12-12T09:17:21.530 回答
2
(?<!\G".{0,99999})\s|(?<=\G".{0,99999}")\s

这将匹配没有被双引号包围的空格。我必须使用 min,max {0,99999} 因为 Java 不支持 * 和 + 在后视。

于 2010-09-15T03:33:39.260 回答
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"
于 2008-12-14T06:02:47.960 回答
1

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."
于 2008-12-14T06:16:35.920 回答
1

我喜欢 Marcus 的方法,但是,我对其进行了修改,以便允许引号附近的文本,并支持 " 和 ' 引号字符。例如,我需要 a="some value" 而不将其拆分为 [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"
于 2013-02-21T20:27:48.477 回答
1

Jan 的方法很棒,但这是另一种记录。

如果您确实想按照标题中所述进行拆分,将引号保留在"will be"and中'regular expression',那么您可以使用这种方法,该方法直接匹配(或替换)模式,但在 s1、s2、s3 等情况下除外

正则表达式:

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

左边的两个交替匹配完成'quoted strings'"double-quoted strings"。我们将忽略这些匹配。右侧匹配并捕获第 1 组的空格,我们知道它们是正确的空格,因为它们没有被左侧的表达式匹配。SplitHere我们用then split on替换那些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
于 2014-05-27T01:46:21.247 回答
1

如果您使用的是 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
于 2017-11-23T11:12:35.763 回答
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 个字符和空格右侧的所有字符既不是单引号也不是双引号时,在空格处拆分

改编自原始帖子 (仅处理双引号)

于 2020-04-09T11:58:35.377 回答
0

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

于 2008-12-14T06:31:27.497 回答
0

对 Jan 接受的答案进行了一些希望有用的调整:

(['"])((?:\\\1|.)+?)\1|([^\s"']+)
  • 允许在带引号的字符串中转义引号
  • 避免重复单引号和双引号的模式;如果需要,这也简化了添加更多引用符号的过程(以另外一个捕获组为代价)
于 2013-03-11T23:47:51.253 回答
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]);
        }
    }
于 2016-09-12T14:01:17.297 回答
0

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

using System.Text.RegularExpressions;

var args = Regex.Matches(command, "[^\\s\"']+|\"([^\"]*)\"|'([^']*)'").Cast<Match>
().Select(iMatch => iMatch.Value.Replace("\"", "").Replace("'", "")).ToArray();
于 2019-07-22T18:30:10.627 回答