49

我有如下技术字符串:

"The thing P1 must connect to the J236 thing in the Foo position."

我想用正则表达式匹配那些只有大写的单词(即这里P1J236)。问题是当它是一个字母单词时,我不想匹配句子的第一个字母。

例如,在:

"A thing P1 must connect ..." 

P1只想要,而不是Aand P1。通过这样做,我知道我可能会错过一个真正的“单词”(例如 in "X must connect to Y"),但我可以忍受它。

另外,如果句子都是大写的,我不想匹配大写单词。

例子:

"THING P1 MUST CONNECT TO X2."

当然,理想情况下,我想在这里匹配技术词P1X2,但是由于它们在全大写句子中“隐藏”,并且由于这些技术词没有特定的模式,所以这是不可能的。再次,我可以忍受它,因为全大写的句子在我的文件中并不常见。

谢谢!

4

6 回答 6

78

在某种程度上,这会因您使用的 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是在我们的主子句之前不得存在的表达式。在这里,该表达式是简单^的,或行首,因此交替的这一侧翻译为“任何由单个大写字母组成的不在行首的单词”。

好的,所以我们正在匹配不在行首的单字母大写单词。我们仍然需要匹配由所有数字和大写字母组成的单词。

这由交替中第二个表达式的相对较小部分处理:\b[A-Z0-9]+\b\bs 代表单词边界,将一个[A-Z0-9]+或多个数字和大写字母匹配在一起。

表达式的其余部分由其他环视组成。(?<!^[A-Z0-9 ]*)是另一个否定的lookbehind,表达式为^[A-Z0-9 ]*。这意味着前面的内容不能全部是大写字母和数字。

第二个环视是(?![A-Z0-9 ]$),这是一个负前瞻。这意味着后面的内容不能全部是大写字母和数字。

因此,总的来说,我们正在捕获所有大写字母和数字的单词,并且从行首排除一个字母的大写字符以及所有大写行中的所有内容。

这里至少有一个弱点,第二个替换表达式中的环视是独立的,所以像“A P1 应该连接到 J9”这样的句子将匹配 J9,但不匹配 P1,因为 P1 之前的所有内容都是大写的。

可以解决这个问题,但它几乎会使正则表达式的长度增加三倍。尝试在单个正则表达式中做这么多事情很少,如果有的话,是合理的。您最好将工作分解为多个正则表达式或您选择的编程语言中的正则表达式和标准字符串处理命令的组合。

于 2011-01-04T20:59:56.180 回答
5

为什么你需要在一个怪物正则表达式中做到这一点?您可以使用实际代码来实现其中的一些规则,如果这些要求以后发生变化,这样做会更容易修改。

例如:

if(/^[A-Z0-9\s]*$/)
    # sentence is all uppercase, so just fail out
    return 0;

# Carry on with matching uppercase terms
于 2011-01-04T21:00:41.157 回答
5

也许您可以先运行这个正则表达式来查看该行是否全部大写:

^[A-Z \d\W]+$

只有当它是一条线时才会匹配THING P1 MUST CONNECT TO X2.

否则,您应该能够使用以下方法提取单个大写短语:

[A-Z][A-Z\d]+

这应该匹配“P1”和“J236”The thing P1 must connect to the J236 thing in the Foo position.

于 2011-01-04T21:12:24.933 回答
4

不要做 [AZ] 或 [0-9] 之类的事情。做 \p{Lu} 和 \d 代替。当然,这对于基于 perl 的正则表达式风格有效。这包括java。

我建议你不要做一些巨大的正则表达式。首先将文本拆分为句子。然后对其进行标记(分成单词)。使用正则表达式检查每个标记/单词。跳过句子中的第一个标记。事先检查所有标记是否都是大写的,如果是则跳过整个句子,或者在这种情况下更改正则表达式。

于 2013-02-12T16:24:16.297 回答
3

无论如何,我都不是正则表达式大师。但是尝试:

<[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

我不会尝试整个大写句子的加分。呵呵

于 2011-01-04T21:07:30.343 回答
2

对于您建议的第一种情况,您可以使用:'[[:blank:]]+[A-Z0-9]+[[:blank:]]+',例如:

echo "东西 P1 必须连接到 Foo 位置的 J236 东西" | grep -oE '[[:blank:]]+[A-Z0-9]+[[:blank:]]+'

在第二种情况下,也许您需要使用其他东西而不是正则表达式,也许是带有技术词汇字典的脚本......

干杯,费尔南多

于 2011-01-04T21:00:26.487 回答