【问题标题】:How can I find strings that have mixed cased with Perl?如何找到与 Perl 混合大小写的字符串?
【发布时间】:2009-12-09 09:12:14
【问题描述】:

我正在尝试过滤数千个文件,寻找那些包含混合大小写的字符串常量的文件。此类字符串可以嵌入空格中,但本身可能不包含空格。所以以下(包含 UC 字符)是匹配项:

"  AString "   // leading and trailing spaces together allowed
"AString "     // trailing spaces allowed
"  AString"    // leading spaces allowed
"newString03"  // numeric chars allowed
"!stringBIG?"  // non-alphanumeric chars allowed
"R"            // Single UC is a match

但这些不是:

"A String" // not a match because it contains an embedded space
"Foo bar baz" // does not match due to multiple whitespace interruptions
"a_string" // not a match because there are no UC chars

我仍然想匹配包含 both 模式的行:

"ABigString", "a sentence fragment" // need to catch so I find the first case...

我想使用 Perl 正则表达式,最好由 ack 命令行工具驱动。显然,\w\W 不会起作用。似乎 \S 应该匹配非空格字符。我似乎无法弄清楚如何嵌入“每个字符串至少一个大写字符”的要求......

ack --match '\"\s*\S+\s*\"'

是我得到的最接近的。我需要用 something 替换 \S+ 以捕获“至少一个大写(ascii)字符(在非空白字符串的任何位置)”要求.

这在 C/C++ 中编程很简单(是的,Perl,在程序上,无需借助正则表达式),我只是想弄清楚是否有一个正则表达式可以做同样的工作。

【问题讨论】:

  • “混合大小写”是否意味着它必须包含两个大写和小写字母?
  • 其实没有。我添加了一个示例,“R”也应该匹配。谢谢。

标签: regex perl ack


【解决方案1】:

以下模式通过了所有测试:

qr/
  "      # leading single quote

  (?!    # filter out strings with internal spaces
     [^"]*   # zero or more non-quotes
     [^"\s]  # neither a quote nor whitespace
     \s+     # internal whitespace
     [^"\s]  # another non-quote, non-whitespace character
  )

  [^"]*  # zero or more non-quote characters
  [A-Z]  # at least one uppercase letter
  [^"]*  # followed by zero or more non-quotes
  "      # and finally the trailing quote
/x

使用这个测试程序——它使用上面没有/x的模式,因此没有空格和cmets——作为ack-grep的输入(因为ack在Ubuntu上被调用)

#! /usr/bin/perl

my @tests = (
  [ q<"  AString ">   => 1 ],
  [ q<"AString ">     => 1 ],
  [ q<"  AString">    => 1 ],
  [ q<"newString03">  => 1 ],
  [ q<"!stringBIG?">  => 1 ],
  [ q<"R">            => 1 ],
  [ q<"A String">     => 0 ],
  [ q<"a_string">     => 0 ],
  [ q<"ABigString", "a sentence fragment"> => 1 ],
  [ q<"  a String  "> => 0 ],
  [ q<"Foo bar baz">  => 0 ],
);

my $pattern = qr/"(?![^"]*[^"\s]\s+[^"\s])[^"]*[A-Z][^"]*"/;
for (@tests) {
  my($str,$expectMatch) = @$_;
  my $matched = $str =~ /$pattern/;
  print +($matched xor $expectMatch) ? "FAIL" : "PASS",
        ": $str\n";
}

产生以下输出:

$ ack-grep '"(?![^"]*[^"\s]\s+[^"\s])[^"]*[A-Z][^"]*"' try
  [ q<"  AString ">   => 1 ],
  [ q<"AString ">     => 1 ],
  [ q<"  AString">    => 1 ],
  [ q<"newString03">  => 1 ],
  [ q<"!stringBIG?">  => 1 ],
  [ q<"R">            => 1 ],
  [ q<"ABigString", "a sentence fragment"> => 1 ],
my $pattern = qr/"(?![^"]*[^"\s]\s+[^"\s])[^"]*[A-Z][^"]*"/;
  print +($matched xor $expectMatch) ? "FAIL" : "PASS",

使用 C shell 及其衍生版本,您必须摆脱困境:

% ack-grep '"(?\![^"]*[^"\s]\s+[^"\s])[^"]*[A-Z][^"]*"' ...

我希望我可以保留突出显示的匹配项,但这似乎不是allowed

请注意,转义的双引号 (\") 会严重混淆此模式。

【讨论】:

  • 这是一件很美的事情,有点。 ;^)~ 现在如果我能弄清楚如何为 shell 转义它,我可以将它与 ack 一起使用!
  • 只使用单引号。请参阅我修改后的答案。
  • ack-grep 只是 ack 的别名吗?我有 1.88 版的 ack。此外,使用 c-shell,单引号版本失败:[: Event not found.
  • 但是 Bourne shell 似乎可以工作。好的,我有我的答案!谢谢@gbacon!
  • 查看修改后的答案。很高兴为您提供帮助!
【解决方案2】:

您可以使用字符类添加要求,例如:

ack --match "\"\s*\S+[A-Z]\S+\s*\""

我假设ack 一次匹配一行。 \S+\s*\" 部分可以连续匹配多个右引号。它将匹配整个"alfa"",而不仅仅是"alfa"

【讨论】:

  • ack, not awk ;^)~ 它嵌入 Perl 或作为命令行运行,因此使用 Perl 正则表达式:betterthangrep.com。但我当然仍然可以考虑 awk。谢谢。
  • 哦,正如所写,您的不需要大写字符作为非空格字符串中的最后一个字符吗?我需要在非空格字符串中使用 UC char anywhere
  • @Don Wakefield:是的,我有点想知道新的 AWK --match 选项 :) 正则表达式应该可以在 Perl 中工作
  • @Don Wakefield:它在[A-Z] 之前和之后都有\S+,因此它不需要字符串末尾的大写
  • 但我认为如果只有一个字符串,它确实需要大写 not 在字符串的末尾,换句话说,“abcD”将不匹配.
猜你喜欢
  • 1970-01-01
  • 2012-06-15
  • 2012-07-20
  • 2012-01-03
  • 2020-01-01
  • 2014-07-23
  • 1970-01-01
  • 2010-10-15
  • 1970-01-01
相关资源
最近更新 更多