如何在不匹配其他字符的情况下正确匹配 C# 中的单词分隔符
本文关键字:分隔符 单词 情况下 不匹配 其他 字符 | 更新日期: 2023-09-27 18:37:18
为新手问题道歉,但 C# 不是我的第一门语言。
我正在尝试在给定的内容中构建单词之间所有分隔符的索引列表,并考虑标点符号。我希望使用正则表达式''b(单词"边界"),但它与我没想到的各种东西相匹配。这是我写的方法:
internal static IList<int> GetBreakIndexesInContent(string content)
{
IList<int> indices = new List<int>();
if (content != null)
{
foreach (Match match in Regex.Matches(content, @"'b"))
{
Console.WriteLine("INDEX:[" + match.Index + "] CHAR:[" + content.Text[match.Index] + "] UNICODE:[" + (int)content.Text[match.Index] + "]");
indices.Add(match.Index);
}
}
return indices;
}
给定以下 100 个字符的字符串:
"Lorem ipsum dolor sit amet, tritani quaestio suscipiantur mea ea, duo et impedit facilisi evertitur."
我希望我的方法生成一个长度为 14 个元素的列表,其中第一个索引将是位置 5,第二个位置是 11,依此类推(忽略位置 26 和 64 处的逗号,以及 99 处的句点)。相反,这是我得到的输出:
//COUNT: [30]
INDEX:[0] CHAR:[L] UNICODE:[76]
INDEX:[5] CHAR:[ ] UNICODE:[32]
INDEX:[6] CHAR:[i] UNICODE:[105]
INDEX:[11] CHAR:[ ] UNICODE:[32]
INDEX:[12] CHAR:[d] UNICODE:[100]
INDEX:[17] CHAR:[ ] UNICODE:[32]
INDEX:[18] CHAR:[s] UNICODE:[115]
INDEX:[21] CHAR:[ ] UNICODE:[32]
INDEX:[22] CHAR:[a] UNICODE:[97]
INDEX:[26] CHAR:[,] UNICODE:[44]
INDEX:[28] CHAR:[t] UNICODE:[116]
INDEX:[35] CHAR:[ ] UNICODE:[32]
INDEX:[36] CHAR:[q] UNICODE:[113]
INDEX:[44] CHAR:[ ] UNICODE:[32]
INDEX:[45] CHAR:[s] UNICODE:[115]
INDEX:[57] CHAR:[ ] UNICODE:[32]
INDEX:[58] CHAR:[m] UNICODE:[109]
INDEX:[61] CHAR:[ ] UNICODE:[32]
INDEX:[62] CHAR:[e] UNICODE:[101]
INDEX:[64] CHAR:[,] UNICODE:[44]
INDEX:[66] CHAR:[d] UNICODE:[100]
INDEX:[69] CHAR:[ ] UNICODE:[32]
INDEX:[70] CHAR:[e] UNICODE:[101]
INDEX:[72] CHAR:[ ] UNICODE:[32]
INDEX:[73] CHAR:[i] UNICODE:[105]
INDEX:[80] CHAR:[ ] UNICODE:[32]
INDEX:[81] CHAR:[f] UNICODE:[102]
INDEX:[89] CHAR:[ ] UNICODE:[32]
INDEX:[90] CHAR:[e] UNICODE:[101]
INDEX:[99] CHAR:[.] UNICODE:[46]
我之所以不只是试图在" "
或以后只过滤 ASCII 32 进行匹配,是因为这需要对不一定在所有单词之间使用空格的外语敏感。另外,因为我不想无意中将多个空格捕获为单独的"分隔符"。
我真的希望'b
能成为真正单词分离的一个很好的标准包罗万象,但事实似乎并非如此。我可以"自己动手",但我希望如果 C# 已经有某种工具来处理这个问题,我能省去重新发明轮子的麻烦。
当然,任何帮助将不胜感激。
谢谢格雷格。
如果正则表达式('w
)中单词字符的定义满足您的需求(请继续阅读),则可以匹配非单词字符(例如,通过使用其反字符类'W
单词之间的内在内容)。解决方案可以像
private static readonly Regex rxWord = new Regex( @"'w+" ) ;
static IEnumerable<string> ParseWords( string s )
{
return rxWord.Matches(s).Cast<Match>().Select( m => m.Value ) ;
}
private static Regex rxNonWord = new Regex( @"'W+" ) ;
private static IEnumerable<string> ParseNonWords( string s )
{
return rxNonWord.Matches(s).Cast<Match>().Select( m => m.Value ) ;
}
但是,从您所说的尝试操作来看,从 CLR 支持的 Unicode 类别中组合字符类或单词分隔符可能会更容易。
此外,使用正则表达式"word"和"non-word"类('w
和'W
)以及它们之间的边界('b
)可能行不通,因为在正则表达式中,"单词"不一定是你认为的那样。 字符类'w
最初是C语言标识符([A-Za-z0-9_]
)中允许的字符集。如果您是使用正则表达式来 grep 符号源代码的 C 程序员,则非常有用。不太适合在文字中翻找文字。
CLR 正则表达式中'w
的当前定义是它匹配以下任何 Unicode 类别中包含的任何字符:
- 李(字母,小写)
- 卢(字母,大写)
- Lt(字母,标题大小写)
- 洛(信,其他)
- Lm (字母、修饰符)
- Nd(数字,十进制数字)
- PC(标点符号、连接器) 此类别包括 10 个字符。这里最常见的一个,至少在英语中,是
_
(0x005F)又名下划线或LOWLINE。
所有要说的是,'w
是写['p{Ll}'p{Lu}'p{Lt}'p{Lo}'p{Lm}'p{Nd}'p{Pc}]
的懒惰方式。
非单词字符类'W
与此相反。这完全等同于说[^'p{Ll}'p{Lu}'p{Lt}'p{Lo}'p{Lm}'p{Nd}'p{Pc}]
.
零宽度锚'b
不"匹配"任何东西:就像它的姐妹^
和$
一样,'b
将匹配锚定到特定位置。在'b
的情况下,那个地方是一个词('w
)和一个非词('W
)字符之间的边界。 'b
有一个表亲,'B
匹配相反:它将匹配锚定在两个单词('w
)或两个非单词('W
)字符之间的边界。
所以。。。
您需要首先提出适合您的问题域的"单词"定义。这比看起来更难:例如,"二十三"是一两个词吗?"前妻"呢?或者像"抽象表现主义"这样的复合词怎么样,根据上下文,它要么是一个词,要么是两个词(你会发现"抽象"、"表现主义"和"抽象表现主义"是字典中的单个条目)。
如果可以定义符合该定义的字符类,则一切都很好。为了匹配单词之间的间隙内容,您所要做的就是定义其反向字符类。
如果一个简单的字符类不适合你,你需要使用各种前瞻/后视断言来匹配你想要的。
我不是故意输入这么长的评论。我想我不妨把它移到一个答案上。
'b
匹配单词和非单词字符之间的所有边界,即'w
和'W
之间的边界,包括字符串开头和第一个字母之间,字母和空格之间(在空格的两侧)等等。
您可能需要将表达式与环顾四周断言相结合,以实现您想要的。
例如
'b(?<=[a-zA-Z])
使用肯定的后视断言来确保仅匹配字母后面的单词边界。但是,这会考虑空格分隔符,我不确定您要这样做,在这种情况下,
'b(?<=[a-zA-Z])(?!'s)
添加了一个附加条件 - 这次是否定的前瞻断言,以确保仅匹配不后跟空格字符的单词边界。
单词边界匹配位置示例:
In Lorem ipsum dolor sit amet,
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^^
所以你可以看到它的比赛比你想象的要多得多。
从技术上讲,边界是一种断言。断言存在于"字符之间"。
当他们坐在角色之间时,他们倾向于向前看或向后看。
所以'b
可能是(?<='w)(?='W|$)
的,也可以是(?<='W|^)(?='w)