正则表达式能实现这个吗?

3
我正在尝试通过正则表达式将字符串拆分为标记,方式如下: 示例#1 输入字符串:'hello' 第一个标记:' 第二个标记:hello 第三个标记:' 示例#2 输入字符串:'hello world' 第一个标记:' 第二个标记:hello world 第三个标记:' 示例#3 输入字符串:hello world 第一个标记:hello 第二个标记:world 即:仅在字符串不在单引号中时拆分字符串,并且单引号应该在它们自己的标记中。
这是我目前的代码:
string pattern = @"'|\s";
Regex RE = new Regex(pattern);
string[] tokens = RE.Split("'hello world'");

这将适用于示例#1和示例#3,但不适用于示例#2。 我想知道是否理论上存在一种使用正则表达式实现我想要的方法。


你会对字符串“hello 'to the world'”有什么期望?(即混合引号和非引用单词) - Paolo
token1: 你好 token2: ' token3: 世界 token4: ' - Shai UI
你可以先使用引号字符串正则表达式进行分割,然后进一步分割每个字符串。 - Aryabhatta
典型的引用字符串场景,只是以空格分隔而不是逗号。问题是,转义怎么处理?你需要处理其中一个字符串包含嵌入引号的情况吗? - Aaronaught
1
请原谅我可能会有些 "离题" 的评论,因为您显然想使用正则表达式,但是,“使用 c# 正则 split” 函数并使用空格字符定界符拆分格式字符串的“优点”在于:您最终会得到一个数组,这个数组非常容易解析,因为每个单引号 "块" 的起始数组条目将以单引号开头,并且每个单引号块的结尾都将以单引号结尾。我的直觉告诉我,SO 其中一位居住的 Linq 天才很快就会提供一个优雅的 "haiku" 来解决这个问题。 - BillW
显示剩余3条评论
8个回答

5

你可以构建一个简单的词法分析器,它涉及逐个消耗每个标记。因此,你将拥有一系列正则表达式,并在每个点尝试匹配其中之一。如果你的输入不仅仅是非常简单的内容,那么这是最简单和最干净的方法。


是的,但我希望“hello world”作为一个单独的标记。我发现regex.split()在生成标记方面非常好,除了这种情况... - Shai UI
@Shnitzel:那么你应该在词法分析器中定义一个情况,以便在单引号内部消耗更多文本。是的,regex.split()是一个非常简单的选项,从你想要做的事情来看,似乎你需要更强大的工具。此外,你可能想要使用C#中的词法分析器和解析器生成器之一,它们可以让你的生活变得更加轻松。 - Stephen Cross
@clintp:是的,看起来是这样 :) - Stephen Cross

3
使用令牌分解器将其拆分为标记。使用正则表达式查找字符串模式。

2
'[^']+'将匹配单引号内的文本。如果您想对其进行分组,使用(')([^']+)(')。如果没有找到匹配项,则只需使用常规字符串拆分。我认为尝试在一个正则表达式中完成整个操作是没有意义的。
编辑:从您对问题的评论中看来,您实际上希望将此应用于较大的文本块,而不仅仅是像您所示的简单输入。如果是这种情况,那么我认为正则表达式不是您的答案。

没错,你不能创建一个正则表达式来解析未定义数量的标记(至少不是在单个步骤中)。 - Scott Smith

1

你可以先按引号字符串进行分割,然后进一步进行标记化。

foreach (String s in Regex.Split(input, @"('[^']+')")) {
    // Check first if s is a quote.
    // If so, split out the quotes.
    // If not, do what you intend to do.
}

(注:您需要在模式中使用括号,以确保Regex.Split也返回它们)

Split 不会移除引号之间的字符串吗? - Kobi
我不这么认为,但是 .Net 版本之间确实存在差异。我记得曾经利用这个思路,快速编写了一个有效的词法分析器和语法分析器。它可能不是最优的方案,但对于中等长度的字符串似乎已足够好了。 - Aryabhatta
请检查一下您的代码是否删除了引号之间的标记 - Split 不会将分隔符包含在其结果中。 - Kobi
你使用的是哪个版本的.NET?无论如何,我会检查我手头的代码并尽快进行修改。 - Aryabhatta
我正在使用3.5版本,但我相信所有版本都会同意这里。以下是JavaScript版本:alert("hello 'crule' world".split(/'[^']+'/)); - Kobi
我检查了我的代码。该模式在括号中(根据MSDN,是捕获括号)。这适用于.NET 2.0或更高版本。我已编辑答案。 - Aryabhatta

1

虽然不完全符合您的要求,但正则表达式条件可能会在寻找解决方案时有所帮助:

(?<quot>')?(?<words>(?(quot)[^']|\w)+)(?(quot)')

如果找到引号,则匹配直到找到非引号为止。否则查看单词字符。您的结果以“quot”和“words”命名的组形式呈现。


+1 - 我认为这就是楼主所寻找的。这与我的答案类似,但更加复杂(我认为OR在这里更好用)。另外,你曾经有999的声望值。 - Kobi

1

在这里使用Split可能会很困难,但是您可以使用MatchCollection来查找字符串中的所有匹配项:

string str = "hello world, 'HELLO WORLD': we'll be fine.";
MatchCollection matches = Regex.Matches(str, @"(')([^']+)(')|(\w+)");

正则表达式搜索单引号之间的字符串。如果找不到,则取一个单词。
现在有点棘手——.net返回一个Match集合。每个匹配都有几个Group——第一个Group有整个字符串('hello world'),但其余的有子匹配(',hello world,')。此外,你会得到许多空的不成功的组。
你仍然可以轻松地迭代并获取你的匹配。这里是一个使用LINQ的例子:
var tokens = from match in matches.Cast<Match>()
             from g in match.Groups.Cast<Group>().Skip(1)
             where g.Success
             select g.Value;

tokens现在是一个字符串集合:
helloworld'HELLO WORLD'wellbefine


小提示:您可以将“\w+”替换为“\S+”,以保留其他字符。 - Kobi

1

尽管可以分别匹配'和其中的文本,也可以单独匹配文本,但RegExp不允许无限数量的匹配。或者更准确地说,您只能匹配表达式中明确指定的对象。因此,((\w+)+\b)理论上可以逐个匹配所有单词。外部组将正确匹配整个文本,内部组也将正确匹配单独的单词,但您只能引用最后一次匹配。

没有办法匹配一组匹配的结果(奇怪的句子)。唯一可能的方法是先匹配字符串,然后将其拆分为单独的单词。


是的,那就是我在想的...但让我们看看是否有人会提出别的想法;) - Shai UI
根本不是这样。按照你的逻辑,正则表达式不能用于从文本中匹配所有数字,例如。但是,在所有版本上,它们可以相当容易地完成。您不需要为每个字符串使用捕获组。 - Kobi
你不能使用一个正则表达式将每个数字作为单独的匹配项获取。 - poke
为什么你会使用分组呢? - Kobi

0

尝试使用这个正则表达式:

([']*)([a-z]+)([']*)

这个程序会在字符串的开头和结尾找到一个或多个单引号。然后它会找到a-z集合中的一个或多个字符(如果您不将其设置为不区分大小写,则只会找到小写字符)。它将这些字符分组,使得第一组是',第二组(或更多)是由任何非a-z字符分隔的单词,最后一组是单引号(如果存在)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接