智能搜索和替换

Smart search and replace

我有一些代码有几千行代码,其中包含这样的片段

opencanmanager.GetObjectDict()->ReadDataFrom(0x1234, 1).toInt()

我需要转换成其他使用类似语法的库

ReadFromOD<int>(0x1234, 1)

.

基本上我需要搜索 [whatever1]opencanmanager.GetObjectDict()->ReadDataFrom([whatever2]).toInt()[whatever3] 跨越文本文件的所有行,并将它的每一次出现替换为 [whatever1]ReadFromOD<int>([whatever2])[whatever3] 然后对其他一些数据类型执行相同的操作。

手动执行此操作将是几天绝对糟糕的愚蠢工作,但我所知道的任何编辑器的所有自动功能都不允许使用任何智能代码重构工具。

现在我已经使用 GNU AWK 和下面的脚本解决了这个问题

#!/usr/bin/awk -f
BEGIN {
    spl1 = "opencanmanager.GetObjectDict()->ReadDataFrom("
    spl2 = ").to"
    spl2_1 = ").toString()"
    spl2_2 = ").toUInt()"
    spl2_3 = ").toInt()"
    min_spl2_len = length(spl2_3)

    repl_start = "ReadFromOD<"
    repl_mid1 = "QString"
    repl_mid2 = "uint"
    repl_mid3 = "int"
    repl_end = ">("
    repl_after = ")"
}

function replacer(str)
{
    pos1 = index(str, spl1)
    pos2 = index(str, spl2)
    if (!pos1 || !pos2) {
        return str
    }

    strbegin = substr(str, 0, pos1-1)
    mid_start_pos = pos1+length(spl1)

    strkey = substr(str, pos2, min_spl2_len)
    key1 = substr(spl2_1, 0, min_spl2_len)
    key2 = substr(spl2_2, 0, min_spl2_len)
    key3 = substr(spl2_3, 0, min_spl2_len)

    strmid = substr(str, mid_start_pos, pos2-mid_start_pos)
    if (strkey == key1) {
        repl_mid = repl_mid1; spl2_fact = spl2_1;
    } else if (strkey == key2) {
        repl_mid = repl_mid2; spl2_fact = spl2_2;
    } else if (strkey == key3) {
        repl_mid = repl_mid3; spl2_fact = spl2_3;
    } else {
        print "ERROR!!! Found", spl1, "but not any of", spl2_1, spl2_1, spl2_3 "!" > "/dev/stderr"
        exit EXIT_FAILURE
    }
    str_remainder = substr(str, pos2+length(spl2_fact))
    return strbegin repl_start repl_mid repl_end strmid repl_after str_remainder
}

{
    resultstr = [=12=]
    do {
        resultstr = replacer(resultstr)
        more_spl = index(resultstr, spl1) || index(resultstr, spl2)
    } while (more_spl)
    print(resultstr)
}

一切正常,但仍然让我有些烦恼。我的解决方案对于必须非常常见并且必须有一个简单的标准解决方案的工作来说仍然感觉有点太复杂,但出于某种原因我不知道。

我准备放手,但如果您知道更优雅、更快速的单行解决方案或智能代码修改问题的一些特定工具,那么我绝对想知道。

如果 sed 是一个选项,您可以尝试此解决方案,该解决方案应匹配来自输入的两个输出示例。

$ cat input_file
opencanmanager.GetObjectDict()->ReadDataFrom(0x1234, 1).toInt()

power1 = opencanmanager.GetObjectDict()->ReadDataFrom(0x1234, 1).toInt() * opencanmanager.GetObjectDict()->ReadDataFrom(0x5678, 1).toUInt() * FACTOR1;
power2 = opencanmanager.GetObjectDict()->ReadDataFrom(0x5678, 1).toUInt() / 2;
$ sed -E 's/ReadDataFrom/ReadFromOD<int>/g;s/int/uint/2;s/(.*= )?[^>]*>([^\.]*)[^\*|/]*?(\*|\/.{2,})?[^\.]*?[^>]*?>?([^\.]*)?[^\*]*?(.*)?/   /' input_file
ReadFromOD<int>(0x1234, 1)

power1 = ReadFromOD<int>(0x1234, 1) * ReadFromOD<uint>(0x5678, 1) * FACTOR1;
power2 = ReadFromOD<int>(0x5678, 1) / 2;
  • 说明

s/ReadDataFrom/ReadFromOD<int>/g - 命令的第一部分进行简单的全局替换,将所有出现的 ReadDataFrom 替换为 ReadFromOD<int>

s/int/uint/2 - 第二部分只会将 int 的第二次出现替换为 uint 如果有

s/(.*= )?[^>]*>([^\.]*)[^\*|/]*?(\*|\/.{2,})?[^\.]*?[^>]*?>?([^\.]*)?[^\*]*?(.*)?/ / - 第三部分利用 sed 分组和反向引用。

  • (.*= )? - 第一组返回后向引用 </code> 捕获所有内容直到 <code>= 字符,? 使其成为有条件的意思不必存在才能匹配剩余的分组。

  • [^>]*> - 这是排除的匹配项,因为它不在括号 () 内。它匹配从 = 字符之后的 space 一直到 > 的所有内容,然后包含文字 > 以排除它。这不是有条件的,必须匹配。

  • ([^\.]*) - 从排除的匹配继续,这将继续匹配直到第一个 . 的所有内容,并且可以通过反向引用 </code> 返回。这不是有条件的,必须匹配。</p> </li> <li><p><code>[^\*|/]*? - 这是一个排除的匹配项,将匹配所有内容,直到文字 *|/。它是有条件的 ? 所以不必匹配。

  • (\*|\/.{2,})? - 从排除的匹配继续,这将继续匹配所有内容,包括 *| / 后跟至少 2 个或更多 {2,} 个字符。可以用反向引用</code>返回,并且是有条件的<code>?

  • [^\.]*?[^>]*?>? - 有条件排除的匹配项。将所有内容匹配到文字 .,然后匹配到 > 的所有内容并包括 >

  • ([^\.]*)? - 匹配句号 . 的条件组。可以用反向引用返回</code>.</p> </li> <li><p><code>[^\*]*? - 已排除。继续匹配到 *

  • (.*)? - 最终 * 之后的所有其他内容都应分组并返回,如果存在 </code> <code> ?