匹配大写字母的正则表达式

4
 def normalized?

    matches = match(/[^A-Z]*/)
    return matches.size == 0

  end

这是我的函数,用于操作字符串,检查字符串是否只包含大写字母。它可以很好地排除不匹配的情况,但是当我对像"ABC"这样的字符串调用它时,它会说没有匹配项,因为显然matches.size为1而不是零。似乎其中有一个空元素。
有人能解释一下吗?

3
如果你使用的 Ruby 版本支持 \p{Lu}, 就应该始终使用它或者是 [\p{Lu}\p{Lt}]。在处理现代文本的代码中,[A-Z] 是一种应该避免使用的反模式和代码异味。 - tchrist
好的,为什么这更可取呢?它在速度上比[A-Z]表现更出色吗? - Samuel
@Samuel - 为了避免Unicode十字军的干扰,如果你只关心包含字母[A-Z]的行,请明确指定。 - user557597
1
Samuel:如果正确性不重要,我可以让任何东西运行得无限快。那又怎样呢?问题不在于 [A-Z] 很慢;而是它是错误的。\p{Lu} 更好,尽管它仍然错过了42个大写字母编码点。 - tchrist
我猜\p{Lu}也适用于像Ä这样的字母吗?不过在我的情况下,我严格只支持A到Z的字母,所以我想知道在我的情况下只使用A-Z会不会更快一些? - Samuel
@Samuel - 如果你特别想匹配 A-Z,使用 Unicode 类(例如 \p{Lu})会产生错误。所以请确保这是你想要的。 - user557597
8个回答

3
您的正则表达式有误 - 如果您只想匹配大写字符串,请使用/^[A-Z]+$/

好的,您能告诉我为什么它是错误的吗? - Samuel
当然 - 在括号([])内部有一个插入符号(^),表示不是 A-Z,星号表示它可能什么也不匹配。因此,我们在正则表达式的末尾加上结束定界符($),并将 * 更改为 +,以确保字符串至少有1个字符长。 - mway
@mway:我认为他非常清楚他的正则表达式是做什么的。这就是为什么他试图检查匹配是否为空。如果是,那么字符串只包含大写字母。这可能是一种过于复杂的方法,但如果MatchData#size做他认为它应该做的事情,那么它就是完全正确的。 - sepp2k
是的,就像他所说的那样,但感谢您提供了一种更合理的正则表达式 :) - Samuel
@mway:恐怕你的语句和正则表达式都是错误的。你不能用那种方式匹配只有大写字母的字符串。 - tchrist
显示剩余2条评论

3
您的正则表达式不正确。 /[^A-Z]*/ 的意思是“匹配零个或多个不在AZ之间的字符,任何位置都可以”。字符串ABC没有任何不在AZ之间的字符,因此它与正则表达式匹配。
请将您的正则表达式更改为/^[^A-Z]+$/。这意味着“匹配一个或多个不在AZ之间的字符,并确保从字符串开头到结尾的每个字符都不在AZ之间”。然后,字符串ABC将不匹配,然后您可以像sepp2k的答案那样检查matches [0] .size或其他内容。

1
如果他有一个不匹配的正则表达式,他肯定不能检查 matches [0] .size,因为 matches 可能是 nil。他当前方法的重点是正则表达式总是匹配的,并且他正在检查它是否匹配了空字符串或更实质的内容。 - sepp2k

2
ruby-1.9.2-p180>   def normalized? s
ruby-1.9.2-p180?>    s.match(/^[[:upper:]]+$/) ? true : false
ruby-1.9.2-p180?>  end
 => nil 
ruby-1.9.2-p180>  normalized? "asdf"
 => false 
ruby-1.9.2-p180>  normalized? "ASDF"
 => true 

2
MatchData#size 返回正则表达式中捕获组的数量加一,因此只有当 i < md.size 时,md[i] 才能访问有效的捕获组。因此,size 返回的值仅取决于正则表达式,而不是匹配的字符串,并且永远不会为0。
您需要使用 matches.to_s.sizematches[0].size

谢谢,你建议我保留我的正则表达式吗? - Samuel
1
@Samuel:我会选择mway的正则表达式,因为说“该字符串仅包含A-Z字符”比说“该字符串不包含任何非A-Z字符”更自然。 - sepp2k

1

这个问题需要一个更清晰的答案。正如tchrist所评论的那样,我希望他能回答。"匹配大写字母的正则表达式"是使用:

/\p{Uppercase}/

正如tchrist所提到的,“与一般类别\p{Uppercase_Letter}(也称为\p{Lu})不同。这是因为存在非字母字符,它们被视为大写字母。”

那么,你会怎样回答这个人的问题呢?\p {Uppercase}将匹配单个大写Unicode光谱字母。他想确保字符串全部是 大写 字母。我猜一个简单的解决方法是先匹配单个\P{Uppercase}然后失败。 - user557597

0

只有一个正则表达式可以定义一个仅由全部大写字母组成的字符串:

def onlyupper(s)
(s =~ /^[A-Z]+$/) != nil
end

真值表:

/[^A-Z]*/:
 Testing  'asdf'     matched  'asdf'     length  4
 Testing  'HHH'      matched  ''         length  0
 Testing  ''         matched  ''         length  0
 Testing  '-=AAA'    matched  '-='       length  2
--------
/[^A-Z]+/:
 Testing  'asdf'     matched  'asdf'     length  4
 Testing  'HHH'      matched  nil
 Testing  ''         matched  nil
 Testing  '-=AAA'    matched  '-='       length  2
--------
/^[^A-Z]*$/:
 Testing  'asdf'     matched  'asdf'     length  4
 Testing  'HHH'      matched  nil
 Testing  ''         matched  ''         length  0
 Testing  '-=AAA'    matched  nil
--------
/^[^A-Z]+$/:
 Testing  'asdf'     matched  'asdf'     length  4
 Testing  'HHH'      matched  nil
 Testing  ''         matched  nil
 Testing  '-=AAA'    matched  nil
--------
/^[A-Z]*$/:
 Testing  'asdf'     matched  nil
 Testing  'HHH'      matched  'HHH'      length  3
 Testing  ''         matched  ''         length  0
 Testing  '-=AAA'    matched  nil
--------
/^[A-Z]+$/:
 Testing  'asdf'     matched  nil
 Testing  'HHH'      matched  'HHH'      length  3
 Testing  ''         matched  nil
 Testing  '-=AAA'    matched  nil
--------

@tchrist 哦,对了,A-Z是什么意思?我知道它不是a-z。请尽量保持主题,即A-Z! - user557597
2
你的函数错误地假设[A-Z]足以保证某些内容完全是大写字母:它错过了ᴇ,ᴋ,Á,Æ,Þ,Ȝ,İ,Ə,Ⅻ等,而这些仅仅是拉丁文! A-Z显然是错误的:它错过了968个大写代码点。 必须使用\p{Uppercase}来保证。请注意,派生的二进制属性\p{Uppercase}与一般类别\p{Uppercase_Letter}(也称为\p{Lu})不同。这是因为存在非字母计数为大写字母,例如罗马数字如Ⅷ和带圈字母如Ⓐ都是\p{Uppercase}但不是\P{Lu} - tchrist
询问者请求关于如何匹配大写字母的信息。请正确回答问题。 - tchrist
我猜他没有,因为他只关心匹配A-Z。问题回答正确。 - user557597
在你重命名函数之前,它说“onlyupper”时是在撒谎。我已经解释了“only upper”的实际含义。你所写的是完全不正确的。 - tchrist
@tchris - 好的,我被拉到这里因为一次踩票。现在是2017年。仔细看了一下你的评论。让我们在这里更清楚一些。首先,在Unicode 8中,由UCD支持,现在有1751个_Uppercase_属性的cp。我相信第9版有更多。其次,Uppercase_和_Lu之间的差异是_Lu_没有的120个cp的净负面。我从我的引擎中获取了这个信息:[^\P{Uppercase=Yes}\p{General_Category_Mask=Uppercase_Letter}]+_。第三个,最后一个,引擎不提供这些信息,它是由支持数据库提供的。关键注意点是流动性... - user557597

0

正则表达式中的*表示匹配任意数量的非大写字符,包括零个。因此它总是匹配任何内容。解决方法是去掉*,那么它将不能匹配只包含大写字符的字符串。(如果不允许空字符串,则需要进行不同的测试。)


0
如果您想知道输入字符串是否完全由英文大写字母组成,即A-Z,则必须删除Kleene星号,因为它将在任何输入字符串中的每个单个字符之前和之后匹配(零长度匹配)。语句!s[/ [^ A-Z] /] 告诉您是否没有非A到Z字符的匹配:
irb(main):001:0> def normalized? s
irb(main):002:1>     return !s[/[^A-Z]/]
irb(main):003:1> end
=> nil
irb(main):004:0> normalized? "ABC"
=> true
irb(main):005:0> normalized? "AbC"
=> false
irb(main):006:0> normalized? ""
=> true
irb(main):007:0> normalized? "abc"
=> false

当仍使用否定的 A-Z 类时移除 * 量词,仍在匹配结果上放置一个 ! 条件时,仍会匹配空字符串。当空字符串并不代表由字母 A-Z 构成的字符串时,空字符串返回 true。请参见我上面发布的真值表。 - user557597
它有点类似于 !/[^A-Z]+/ - user557597
空字符串应该明确地返回 true,因为@Samuel的需求是“检查一个字符串是否只包含大写字母”。空字符串“仅包含大写字母”。语句!s[/[^A-Z]/]告诉你是否没有非A到Z字符的匹配。这是正确的答案,因为“大写字母”意味着A到Z。 - Staffan Nöteberg
@Staffan「空字符串只包含大写字母。」让我们来测试一下这个理论:"" =~ /[A-Z]/。不,空字符串什么都没有。 - user557597
@sln 这是一个定义问题,我们似乎不太一致。 :-) 我仍然认为“没有字符的字符串”是“仅大写字符串”的一种类型。要说它不是“仅大写字符串”,我们需要至少有一个不是“大写”的字符——一个反证。/[A-Z]/测试是否至少有一个大写字母,这与“仅大写”不同。但是,这是一个子讨论。 :-) - Staffan Nöteberg
@Staffan 如果空字符串只包含A-Z字母,则应该与/[A-Z]/正向匹配,但实际上并没有。相反,"ABC" =~ /^$/不匹配,但我们不会说"ABC"是空字符串。我认为没有什么可以作为一切的子集而存在,这将使(nothing == something)成立。Nothing可能在质量上有益,但它在测试某些东西时是单独的信息。 - user557597

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