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


114

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

输入示例:

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


您实际上是在使用“ split”方法,还是在Matcher上使用“ find”方法循环就足够了?
埃里克森

9
“现在他有两个问题”

Answers:


251

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

[^\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
Jan,感谢您的回复。顺便说一句,我是EditPad的忠实拥护者。
carlsz

如果我想在字符串中允许使用转义引号\"怎么办?
星期一

3
这个答案的问题是报价无与伦比:John's mother结果分成[John, s, mother]
leonbloy 2014年

2
要解决leonbloy概述的问题,您可以对操作数进行重新排序,并省略whitespace-group中的引号:"([^"]*)"|'([^']*)'|[^\s]+
Ghostkeeper

1
在此答案和其他答案的基础上,以下正则表达式允许在引号内转义字符:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|[^\s]+。参见stackoverflow.com/questions/5695240/…–
Limnic

15

在StackOverflow上有几个问题,使用正则表达式可以在各种情况下涵盖相同的问题。例如:

更新:示例正则表达式以处理单引号和双引号字符串。参考:除了在引号内时,如何拆分字符串?

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

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

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

请注意,尽管您可以通过替换字符串来删除引号字符,也可以将引号字符本身包括在匹配的值中,或者将正则表达式修改为不包括引号字符。我现在将其留给读者或其他张贴者作为练习,因为凌晨2点为时已晚,以至于不再正则表达式了;)


我认为您的正则表达式允许使用不匹配的引号,例如“将是”和“正则表达式”。
Zach Scrivena

@Zach-您说得对,它确实...已对其进行更新以解决此问题,以防万一
Jay


3

到目前为止,Jan Goyvaerts的正则表达式是我找到的最好的解决方案,但是还会创建空(空)匹配项,他将其排除在程序外。这些空匹配也可从正则表达式测试器(例如rubular.com)中显示。如果转向搜索(首先查找引用的部分,然后查找空格分隔的单词),则可以使用以下方法一次完成搜索:

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


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"

您的解决方案不处理单引号字符串,这是Carl示例的一部分。
Jan Goyvaerts

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

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"

1

Jan的方法很棒,但这是有记录的。

如果您实际上想按标题中所述拆分,将引号括在"will be"和中'regular expression',则可以使用此方法,该方法直接匹配(或替换)模式,但情况s1,s2,s3等除外

正则表达式:

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

左两个交替匹配complete '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

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

0

我可以肯定地说,仅使用正则表达式是不可能的。检查其他标签中是否包含某些内容是解析操作。这似乎与尝试使用正则表达式解析XML一样,都是同样的问题-无法正确完成。您可以通过重复应用与引用的字符串匹配的非贪婪,非全局正则表达式来获得所需的结果,然后,一旦找不到其他内容,请将其在空格处分割开...问题,包括跟踪所有子字符串的原始顺序。最好的选择是编写一个非常简单的函数,该函数遍历字符串并提取所需的标记。


使用正则表达式是可能的,请参阅我链接到的一些示例。对此有一些变化,我在SO上看到了几个类似的问题,这些问题可以通过正则表达式解决。
杰伊

1
知道何时不使用正则表达式才比创建(?:(['“])(。*?)(?<!\)(?> \\\)* \ 1 |([ ^ \ s] +))
Rene

0

一对夫妇希望对Jan的已接受答案有所帮助:

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

这会打断带有撇号的单词,例如you're
设计者:Adrian

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]);
        }
    }

您实际上应该添加一些解释,说明为什么应该这样做-您还可以添加代码以及代码本身中的注释-以当前形式,它没有提供任何可以帮助社区其他成员理解什么的解释。您确实解决/回答了问题。这对于已经有答案的问题尤为重要。
ishmaelMakitla

0

以下返回一个参数数组。参数是变量“命令”,用空格分隔,除非用单引号或双引号引起来。然后修改匹配项以删除单引号和双引号。

using System.Text.RegularExpressions;

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

2
您可以在回答中添加一点解释,以便其他人更容易理解吗?理想情况下,我们希望避免仅使用代码的答案。
雅克兹

0

第一个使用Liner.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个字符和右侧的所有字符都不是单引号或双引号,则在空格处将其分隔

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.