仅当字符串中有小写时才匹配所有大写单词,并带有一个正则表达式

时间:2014-02-18 01:22:43

标签: regex perl pcre

我偶然发现了这个看似微不足道的问题,我坚持不懈。我有一个字符串,其中我想匹配在一个正则表达式所有大写单词如果字符串中的某个地方至少有一个小写字母。

基本上,我想要这些行中的每一行(我们可以考虑将单独的正则表达式应用于每一行,不需要一些多行处理)来输出:

ab ABC          //matches or captures ABC
ab ABC 12 CD    //matches or captures ABC, CD
ABC DE          //matches or captures nothing (no lowercase)
ABC 23 DE EFG a //matches or captures ABC, DE, EFG
AB aF DE        //matches or captures AB, DE

我使用PCRE作为正则表达式(我知道其他一些口味允许可变长度的后视)。

评论后更新

显然,如果我使用多个正则表达式或我用来调用正则表达式的程序语言,有很多简单的解决方案(例如,首先通过查找小写字母来验证字符串,然后将所有大写单词与两个不同的正则表达式匹配)

我的目标是找到一种方法来使用一个正则表达式。

我对此约束没有技术要求。如果你不得不,或好奇,或者我试图提高我的正则表达能力,那就把它作为一种风格的练习:任务似乎(起初)如此简单,我想知道一个正则表达式单独就可以实现它。如果不能,我想了解原因。

或者如果它可以,但正则表达式不是为这类任务而设计的,我希望我知道为什么 - 或者至少是什么“这些不适合的任务”,以便我可以选择正确的解决方案遇见他们。


那么,它在一个正则表达式中是否可行?

5 个答案:

答案 0 :(得分:1)

<强>更新
因此,\G最初设置为位置0处的匹配条件 这意味着在多线模式下,BOS必须是一个特例 即使BOString是BOLine,如果断言(?= ^ .* [a-z] )失败,则 \G最初设置为匹配(默认?),并且未经验证即可找到UC字。

(?|(?=\A.*[a-z]).*?\b([A-Z]+)\b|(?!\A)(?:(?=^.*[a-z])|\G.*?\b([A-Z]+)\b))

更新2 张贴后代。
在与@Robin讨论之后,上面的正则表达式可以重构为:

 #  (?:(?=^.*[a-z])|(?!\A)\G).*?\b([A-Z]+)\b

 (?:
      (?= ^ .* [a-z] )        # BOL, check if line has lower case letter
   |                        # or
      (?! \A )                # Not at BOS (beginning of string, where \G is in a matched state)
      \G                      # Start the match at the end of last match (if previous matched state)
 )
 .*? \b 
 ( [A-Z]+ )              # (1), Found UC word
 \b     

Perl测试用例:

$/ = undef;

$str = <DATA>;

@ary = $str =~ /(?:(?=^.*[a-z])|(?!\A)\G).*?\b([A-Z]+)\b/mg;

print "@ary", "\n-------------\n";

while ($str =~ /(?:(?=^.*[a-z])|(?!\A)\G).*?\b([A-Z]+)\b/mg)
{
   print "$1 ";
}

__DATA__
DA EFR
ab ABC
ab ABC 12 CD
ABC DE  t
ABC 23 DE EFG a

输出&gt;&gt;

ABC ABC CD ABC DE ABC DE EFG
-------------
ABC ABC CD ABC DE ABC DE EFG

答案 1 :(得分:1)

愚蠢的问题值得愚蠢的答案。

/(?{ @matches = m{\b\p{Lu}+\b}g if m{\p{Ll}} })/;

测试:

use strict;
use warnings;
use feature qw( say );

while (<DATA>) {
   chomp;

   local our @matches;
   /(?{ @matches = m{\p{Lu}+}g if m{\p{Ll}} })/;

   say "$_: ", join ', ', @matches;
}

__DATA__
ab ABC
ab ABC 12 CD
ABC DE
ABC 23 DE EFG a

现在我答应了这个愚蠢的回答:

my @matches = /
   \G
   (?: (?! ^ )
   |   (?= .* \p{Ll} )
   )
   .*? ( \b \p{Lu}+ \b )
/sg;

浓缩为

my @matches = /\G(?:(?!^)|(?=.*\p{Ll})).*?(\b\p{Lu}+\b)/sg;

在字符串的开头,它会向前看小写字母。在其他任何地方,由于我们已经检查过,因此无需检查。

答案 2 :(得分:1)

我不确定是否可以做到,但这里有一些背景信息,解释“为什么?”一点点。

正则表达式旨在匹配 regular languages ,最初,这就是他们所能做的一切。事实上,常规语法是最简单的,并非完全无关紧要;例如,大多数现代计算机语言使用非常规语法。 (尤其参见this section。)

因此,正则表达式可以描述什么样的语言是有限的,例如,用一些简单的英语句子描述的内容就更有限了。

Chomsky hierarchy是一种将语言分类为不同表达水平的方法。请注意,常规语法一直在底部,大多数有用(编程)语言是Type 3或borderline Type-3(即添加了少量Type-3部分)。这是由于一个简单的事实:我们的大脑非常能够处理上下文敏感(Type-3)语法,甚至是复杂语法(因此我们希望编程语言功能强大)。但是,上下文敏感语法的计算机解析器比Type-2的计算机解析器要慢得多(所以,我们希望编程语言的功能有限!

对于预期会很快匹配的正则表达式,限制其整体表现力更为重要。但是,通过编写两个或多个添加了一些控制结构的正则表达式,您可以有效地将它们扩展为比常规表达式解析器更强大。

答案 3 :(得分:-1)

也许我们过度思考:

#! /usr/bin/env perl
#
use strict;
use feature qw(say);
use autodie;
use warnings;
use Data::Dumper;

while ( my $string = <DATA> ) {
    chomp $string;
    my @array;
    say qq(String: "$string");
    if ( @array = $string =~ /(\b[A-Z]+\b)/g ) {
        say qq(String groups: ) . join( ", ", @array ) . "\n";
    }
}

__DATA__
ab ABC
ab ABC 12 CD
ABC DE
ABC 23 DE EFG a
AB aF DE
ADSD asd ADSD
asd ADSDSD
SDSD SDD SD
SSDD SDS asds

输出:

String: "ab ABC"
String groups: ABC

String: "ab ABC 12 CD"
String groups: ABC, CD

String: "ABC DE"
String groups: ABC, DE

String: "ABC 23 DE EFG a"
String groups: ABC, DE, EFG

String: "AB aF DE"
String groups: AB, DE

String: "ADSD asd ADSD"
String groups: ADSD, ADSD

String: "asd ADSDSD"
String groups: ADSDSD

String: "SDSD SDD SD"
String groups: SDSD, SDD, SD

String: "SSDD SDS asds"
String is groups: SSDD, SDS

我错过了什么吗?

答案 4 :(得分:-2)

一个正则表达式:

@words = split (/[a-z]+/, $_);