【问题标题】:Perl search / replace with capture group on CLIPerl 搜索/替换 CLI 上的捕获组
【发布时间】:2023-04-08 12:24:01
【问题描述】:

PHP 的 strlen() 函数不支持 UTF-8,因此我想将 strlen() 的每个用法与其支持 UTF-8 的对应项交换:mb_strlen()。但是,mb_strlen() 需要一个额外的参数:

$length = strlen($someString);
$length = mb_strlen($someString, 'UTF-8');

如果没有第二个参数,一个简单的 Perl 正则表达式将处理交换:

$ find . -name '*' -print0 | xargs -0 perl -pi -e 's/strlen/mb_strlen/g'

我尝试使用捕获组和反向引用,但 VIM 样式的语法要么不支持(在最近的 Ubuntu 上),要么我无法弄清楚。我已经尝试了几种变体但没有成功:

$ find . -name '*' -print0 | xargs -0 perl -pi -e 's/strlen\((\.*)\)/mb_strlen\($1, "UTF-8"\)/g'

此外,strlen() 内部可能有诸如 trim() 之类的函数,所以我必须使这个贪婪,但我不确定贪婪运算符应该准确地去哪里。这个正则应该怎么写?

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    这比最初看起来要困难得多。您需要:

    1. 正确解析表达式,包括表达式的多行版本。
    2. 作弊

    我会去作弊。

    大多数 strlen() 调用都非常简单,剩下的少数可以手动替换。而且您是在某种版本控制下执行此操作的,不是吗:

    简单:strlen("foo"), strlen($bar)

    # Match simple quoted strings - no embedded quotes
    s/strlen\((["'][^"']*["'])\)/mb_strlen($1, 'UTF-8')/g
    # Match simple variables - no method calls etc
    s/strlen\((\$\w+)\)/mb_strlen($1, 'UTF-8')/g
    

    处理数组变量、函数和方法调用以及其他表达式变得更加复杂,但是看看在这两个基本替换之后还剩下多少。

    【讨论】:

    • 为什么不在函数内部使用[^()]*进行匹配呢?这将非常安全,并且可以捕获这两种情况以及更多情况。
    • 打破 strlen(')') - 我说这比看起来更难:-)
    • 有多少代码真正占用了字符串文字的长度?我不认为我曾经这样做过。即便如此,包含右括号的文字也只是一个很小的子集。你的观点是正确的,但这是我在对代码进行搜索和替换时愿意忍受的失败案例。
    【解决方案2】:

    通过指定\.*,正则表达式将匹配0个或多个文字'.'s。

    省略\后试试:

    s/strlen\((.*)\)/mb_strlen($1, "UTF-8")/g
               ^              ^           ^
               NO BACKSLASH   NO BACKSLASH NEEDED
                              AS THIS IS TREATED AS
                              A STRING AND NOT A REGEX
    

    另外,请先尝试在不使用 -i 标志的情况下对其进行测试,以确保您对替换感到满意,否则您的文件将被原地修改。

    【讨论】:

    • 如果您在同一行上有任何其他括号表达式,这将中断。
    • @RichardHuxton :当然,除了最简单的情况之外,它会中断。我的目的是展示 OP 的正则表达式有什么问题。
    【解决方案3】:

    在一般情况下,您的问题无法通过简单的正则表达式解决。考虑以下示例:

    if (strlen($var) > 0)
    
    $total_length = strlen($thing1) + strlen($thing2);
    
    strlen($var);   #Don't use trim() here because it was already trimmed.
    
    some_other_function(strlen($foo) + 2);
    

    这些都不适用于您的正则表达式,因为.* 会贪婪地捕获所有内容,直到行中的最后一个右括号。正确执行此操作的唯一方法是检查平衡括号,这在正则表达式中并非易事(尽管在技术上可以使用 Perl 的扩展正则表达式功能,但这绝非易事)。

    如果您认为您不会遇到上述很多情况,那么只需使用其他建议的解决方案之一并检查错误。或者您可以这样做来捕获所有没有括号的简单案例:

    s/\bstrlen\(([^()]*)\)/mb_strlen($1, "UTF-8")/g
    

    (注意,我还添加了\b 以确保它从单词边界开始。这将阻止您重复替换已经是mb_strlen 的内容)

    但是,有一个简单的快速破解解决方案应该适用于所有情况:创建您自己的名为 my_mb_strlen 的 PHP 函数,或者在添加附加参数时调用 mb_strlen 的其他函数。然后,您可以执行更简单的搜索并仅替换函数名称,将strlen 替换为my_mb_strlen

    【讨论】:

      【解决方案4】:
      find . -type f|xargs perl -pi -e 's/strlen\(([^\)]*)\)/mb_strlen($1,'UTF_8')/g'
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-10-08
        • 1970-01-01
        • 2019-05-05
        • 1970-01-01
        • 1970-01-01
        • 2015-08-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多