正则表达式,用于在未被单引号或双引号括起时使用空格分割字符串

时间:2008-12-14 05:18:31

标签: java regex split

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

示例输入:

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

15 个答案:

答案 0 :(得分:223)

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

[^\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 :(得分:12)

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

UPDATE :示例正则表达式,用于处理单引号和双引号字符串。参考: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.

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

答案 2 :(得分:5)

如果要在字符串中允许转义引号,可以使用以下内容:

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

引用字符串为第2组,单引号字为第3组。

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

答案 3 :(得分:3)

来自Jan Goyvaerts的正则表达式是我迄今为止找到的最佳解决方案,但也创建了空(空)匹配,他在他的程序中排除了。这些空匹配也来自正则表达式测试人员(例如rubular.com)。 如果你转动搜索周围(首先查找引用的部分而不是空格的单词),那么你可以用以下方法完成:

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

答案 4 :(得分:2)

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

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

答案 5 :(得分: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"

答案 6 :(得分: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."

答案 7 :(得分: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);

我已经专门添加了&#34; |&lt;(?[\ w \ s] *)&gt; &#34;突出显示您可以指定任何字符串组。 (在这种情况下,我使用&lt;&gt; 进行分组。

输出是:

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

答案 8 :(得分:1)

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

如果您确实想要按照标题中提到的方式进行拆分,请在"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,而不是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

这是一个完整的工作实施(请参阅online demo上的结果)。

{{1}}

答案 9 :(得分:1)

我喜欢Marcus的方法,但是,我修改它以便我可以在引号附近允许文本,并支持“和'引用字符。例如,我需要一个=”某个值“不要将它分成[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"

答案 10 :(得分:0)

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

答案 11 :(得分:0)

使用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个字符和双引号都不是单引号或双引号时,则在空白处拆分

改编自original post (仅处理双引号)

答案 12 :(得分:0)

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

答案 13 :(得分:0)

一对夫妇对Jan的接受答案进行了有益的调整:

(['"])((?:\\\1|.)+?)\1|([^\s"']+)
  • 允许引用字符串中的转义引号
  • 避免重复单引号和双引号的模式;如果需要,这也简化了添加更多引用符号(以另外一个捕获组为代价)

答案 14 :(得分: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]);
        }
    }