正则表达式仅匹配大写“单词”,但有一些例外
Posted
技术标签:
【中文标题】正则表达式仅匹配大写“单词”,但有一些例外【英文标题】:Regex to match only uppercase "words" with some exceptions 【发布时间】:2011-06-03 15:43:34 【问题描述】:我有如下技术字符串:
"The thing P1 must connect to the J236 thing in the Foo position."
我想用正则表达式匹配那些只有大写的单词(即这里的P1
和J236
)。问题是当它是一个字母的单词时,我不想匹配句子的第一个字母。
例如,在:
"A thing P1 must connect ..."
我只想要P1
,而不想要A
和P1
。通过这样做,我知道我可能会错过一个真正的“词”(例如"X must connect to Y"
),但我可以忍受它。
另外,如果句子都是大写的,我不想匹配大写单词。
例子:
"THING P1 MUST CONNECT TO X2."
当然,理想情况下,我想在这里匹配P1
和X2
的技术词,但由于它们在全大写句子中“隐藏”,而且这些技术词没有特定的模式,所以这是不可能的。再次,我可以忍受它,因为全大写的句子在我的文件中并不常见。
谢谢!
【问题讨论】:
所有技术术语都包含数字吗? 无论你做什么,都不要使用像[A-Z]
这样的7位字面量。那是非常 RADIX-50,在过去几十年编写的代码中没有位置。使用适用于任何文本的东西。这至少意味着使用与\w
或[[:alpha:]]
或\pL
或\pAlphabetic
相关的内容,具体取决于您的正则表达式语言和环境。事实上,实现方式差异很大,其中一些在某些平台上可能是合法且正确的,但在其他平台上可能是合法且错误的。
【参考方案1】:
在某种程度上,这会因您使用的 RegEx 的“风格”而异。以下是基于 .NET RegEx,它使用\b
作为单词边界。在最后一个示例中,它还使用了否定环视 (?<!)
和 (?!)
以及非捕获括号 (?:)
不过,基本上,如果术语总是包含至少一个大写字母后跟至少一个数字,您可以使用
\b[A-Z]+[0-9]+\b
对于全大写和数字(总数必须为 2 或更多):
\b[A-Z0-9]2,\b
对于全大写和数字,但至少以一个字母开头:
\b[A-Z][A-Z0-9]+\b
祖父,返回具有大写字母和数字的任意组合,但在行首不是单个字母且不属于全大写行的项目:
(?:(?<!^)[A-Z]\b|(?<!^[A-Z0-9 ]*)\b[A-Z0-9]+\b(?![A-Z0-9 ]$))
细分:
正则表达式以(?:
开头。 ?:
表示——尽管后面的内容在括号中,但我对捕获结果不感兴趣。这称为“非捕获括号”。在这里,我使用的是括号,因为我使用的是交替(见下文)。
在非捕获括号内,我有两个单独的子句,由管道符号 |
分隔。这是交替——就像一个“或”。正则表达式可以匹配第一个表达式或第二个。这里的两种情况是“is this the first word of the line”或“everything else”,因为我们有特殊要求,排除行首的单字母词。
现在,让我们看看交替中的每个表达式。
第一个表达式是:(?<!^)[A-Z]\b
。这里的主要子句是[A-Z]\b
,它是任何一个大写字母后跟一个单词边界,可以是标点符号、空格、换行符等。之前的部分是(?<!^)
,这是一个“负向回溯”。这是一个零宽度断言,这意味着它不会“消耗”字符作为匹配的一部分——在这里理解这一点并不重要。 .NET 中否定后向查找的语法是 (?<!x)
,其中 x 是在我们的主子句之前必须 not 存在的表达式。这里的表达式只是^
,或行首,所以这一侧的交替翻译为“任何由单个大写字母组成的单词,在行的开头不是 。”
好的,所以我们正在匹配不在行首的单字母大写单词。我们仍然需要匹配由所有数字和大写字母组成的单词。
这由交替中第二个表达式的一小部分处理:\b[A-Z0-9]+\b
。 \b
s 代表单词边界,[A-Z0-9]+
将一个或多个数字和大写字母匹配在一起。
表达式的其余部分由其他环视组成。 (?<!^[A-Z0-9 ]*)
是另一个否定的lookbehind,表达式为^[A-Z0-9 ]*
。这意味着前面的内容不能全部是大写字母和数字。
第二个环视是(?![A-Z0-9 ]$)
,这是一个负环视。这意味着后面的内容不能全部是大写字母和数字。
因此,总的来说,我们正在捕获所有大写字母和数字的单词,并从行首排除一个字母的大写字符以及所有大写行中的所有内容。
这里至少有一个弱点,第二个替换表达式中的环视是独立的,所以像“A P1 应该连接到 J9”这样的句子将匹配 J9,但不匹配 P1,因为 P1 之前的所有内容都是大写的。
可以绕过这个问题,但它会使正则表达式的长度几乎增加三倍。尝试在单个正则表达式中做这么多事情很少,如果有的话,是合理的。您最好将工作分解为多个正则表达式,或者在您选择的编程语言中组合正则表达式和标准字符串处理命令。
【讨论】:
谢谢!我的情况是“全大写和数字”。您提出的解决方案的问题是它会匹配“A thing P1 connect to XYZ”中的A。 @Patrick 这些不匹配 A。前三个需要两个或多个字符,最后一个只需要一个或多个,但不能在开头。 对不起,Jay,我没有看到你第一篇文章中的祖父部分。不幸的是,当我尝试使用 preg_replace 时,它返回消息:“警告:preg_replace():编译失败:后向断言在第 1 行的命令行代码中的偏移 32 处不是固定长度” @Patrick 就像我说的,它会因 RegEx 风格而异,我不知道你在使用什么。并非每种类型的 RegEx 都使用相同的符号,也不是每种类型都支持相同的功能。给出的示例基于 .NET Regex;抱歉,它不适合你。 Jay,我能请你解释一下你的“爷爷”的不同部分吗,我正在努力理解它以适应我的 PCRE 风格。再次感谢!【参考方案2】:为什么你需要在一个怪物正则表达式中做到这一点?您可以使用实际代码来实现其中一些规则,如果这些要求以后发生变化,这样做会更容易修改。
例如:
if(/^[A-Z0-9\s]*$/)
# sentence is all uppercase, so just fail out
return 0;
# Carry on with matching uppercase terms
【讨论】:
实际上,我有一组包含在 mysql 表中的正则表达式,我的 php 代码按顺序执行所有这些 preg_replace() 。这就是为什么我不想通过添加 if 来增加复杂性。当然,如果实在不行,我可能会改变主意…… 好问题。逻辑存储在数据库中,因为最终,用户有责任(通过网络表单)输入将应用于特定文本的正则表达式。我的程序循环遍历这些正则表达式并返回匹配项。【参考方案3】:也许你可以先运行这个正则表达式来查看该行是否全部大写:
^[A-Z \d\W]+$
只有当它是像THING P1 MUST CONNECT TO X2.
这样的行时才会匹配
否则,您应该能够使用以下方法提取单个大写短语:
[A-Z][A-Z\d]+
这应该匹配The thing P1 must connect to the J236 thing in the Foo position.
中的“P1”和“J236”
【讨论】:
在所有大写检查中,我认为空格在 \W 中,然后添加 _ 并假设不需要对空字符串进行进一步检查,它可以概括为/^[A-Z\d\W_]*$/
【参考方案4】:
不要做 [A-Z] 或 [0-9] 之类的事情。做 \pLu 和 \d 代替。当然,这对于基于 perl 的正则表达式风格有效。这包括 java。
我建议你不要做一些巨大的正则表达式。首先将文本拆分为句子。然后对其进行标记(分成单词)。使用正则表达式检查每个标记/单词。跳过句子中的第一个标记。事先检查所有标记是否都是大写的,如果是则跳过整个句子,或者在这种情况下更改正则表达式。
【讨论】:
【参考方案5】:无论如何,我都不是正则表达式专家。但是试试:
<[A-Z0-9][A-Z0-9]+>
< start of word
[A-Z0-9] one character
[A-Z0-9]+ and one or more of them
> end of word
我不会尝试整个大写句子的加分。呵呵
【讨论】:
【参考方案6】:对于您建议的第一种情况,您可以使用:'[[:blank:]]+[A-Z0-9]+[[:blank:]]+',例如:
echo "P1 的东西必须连接到 Foo 位置的 J236 的东西" | grep -oE '[[:blank:]]+[A-Z0-9]+[[:blank:]]+'
在第二种情况下,您可能需要使用其他东西而不是正则表达式,也许是带有技术词汇字典的脚本...
干杯,费尔南多
【讨论】:
我赞成这一点,因为我有使用技术术语词典的想法。由于 OP 已经在其他 cmets 中确定了数据库可用,因此使用此类信息找到有趣的术语似乎更有意义,而不是尝试根据不完善的约定来识别它们。 嗯,确实有数据库可用,但我也提到了技术词没有特定的模式。以上是关于正则表达式仅匹配大写“单词”,但有一些例外的主要内容,如果未能解决你的问题,请参考以下文章