0

我正在使用{fmt}库。

不幸的是,几天后我的程序崩溃了,因为我的格式字符串无效。很容易修复 - 但如果还有更多呢?

可以对字符串格式进行编译时检查,这会发现此错误:

// Replace this:
fmt::print("{}",42)
// With this:
fmt::print(FMT_STRING("{}"),42)

我可以在整个代码库中使用大约 500 个打印语句手动执行此操作。

但我想知道 - 有没有办法用正则表达式和 Visual Studio 中的查找/替换来做到这一点?

我尽可能使用.NET RegEx 测试器和一个字符串匹配:

print[(]".*".*[)];

然而,经过数小时的尝试,我仍然无法实现强大的搜索和替换。


2020 年 7 月 4 日更新。

使用我在下面的答案来解决问题。幸运的是,其余的都很完美。

4

2 回答 2

2

在 VSCode 中,这有效:

图案:.*(fmt::print\()"\{\}"(,.*\)).*

替代品: $1FMT_STRING("{}")$2


如果您是 Python 爱好者,那么如果您将 C++ 脚本作为字符串读取,这也适用:

import re

pattern = '.*(fmt::print\()"{}"(.*\)).*'
fmt_snippet = 'fmt::print("{}",42)'
re.sub(pattern, r'\1FMT_STRING("{}")\2', fmt_snippet)
于 2020-07-02T12:50:17.603 回答
0

扩展 Mark Moretto 的出色回答:

在 Visual Studio 2019 中,这在我的整个 C++ 代码库中运行良好:

替换这个:

(.*print[(])(".*?")(.*[)];)

有了这个:

$1FMT_STRING($2)$3

然后重复这个来处理fmt::format

(.*format[(])(".*?")(.*[)];)

对于全局 Search'n'Replace,确保打开的RegEx图标.*

我使用.NET Regex Tester来形成表达式。

它在大约 5 秒内正确修复了 505 个实例中的 500 个,但它确实因我手动修复的这种事情而绊倒:

// Required manual fix (shift rogue bracket away from end). 
auto y = format("Test=\"{}\""), 42); 

说明(可选)

我一直发现 RegEx 表达式非常复杂,但是通过这个示例的工作不知何故在我的脑海中打开了一个灯泡开关。

  1. (.*print[(])匹配从行首到开头的任何内容print(并替换为$1.
  2. (".*?")匹配从开头到结尾的引号,并替换为$2.
  3. (.*[)];)将所有内容与结尾匹配);,并替换为$3.
  4. $1FMT_STRING($2)$3插入FMT_STRING()正确的位置。

笔记:

  • 关于第 1 点:
    • 注意使用[(]来表示文字(
    • 注意使用.*. 这是一个通配符,.表示任何字符并*表示任何重复次数。
    • 它也将匹配在fmt::print(. 需要不同的正则表达式来处理format(fmt::format((见上文)。
  • 关于第 2 点:
    • 注意使用?来表示它应该在它看到的第一个结束引号处停止(即“非贪婪匹配”)。

附录 A:测试用例(可选)

将以下内容粘贴Input Tests Cases.NET Regex Tester中,以突出显示语法并更轻松地编辑表达式以稍微修改它。

输入测试用例:

// Test non-namespace match.
print("Hello, world!");
print("Test: {}", 42);
print("Test: {}: {}", 42, "A");
print("Test: {:0}: {} : {}", 42, "A", myVariable);
print("{}, {}, {}", 42, "A", "B");
    print("Hello, world!");
    print("Test: {}", 42);
    print("Test: {}: {}", 42, "A");
    print("Test: {:0}: {} : {}", 42, "A", myVariable);
    print("{}, {}, {}", 42, "A", "B");

// Test namespace match.
fmt::print("Hello, world!");
fmt::print("Test: {}", 42);
fmt::print("Test: {}: {}", 42, "A");
fmt::print("Test: {:0}: {} : {}", 42, "A", myVariable);
fmt::print("{}, {}, {}", 42, "A", "B");
    fmt::print("Hello, world!");
    fmt::print("Test: {}", 42);
    fmt::print("Test: {}: {}", 42, "A");
    fmt::print("Test: {:0}: {} : {}", 42, "A", myVariable);
    fmt::print("{}, {}, {}", 42, "A", "B");

// Test compatibility with existing (should be no change).
​fmt::print(FMT_STRING("Hello, world!"));
​fmt::print(FMT_STRING("Test: {}"), 42);
​fmt::print(FMT_STRING("Test: {}: {}"), 42, "A");
​fmt::print(FMT_STRING("Test: {:0}: {} : {}"), 42, "A", myVariable);
​fmt::print(FMT_STRING("{}, {}, {}"), 42, "A", "B");
​    fmt::print("Hello, world!");
​    fmt::print(FMT_STRING("Test: {}"), 42);
​    fmt::print(FMT_STRING("Test: {}: {}"), 42, "A");
​    fmt::print(FMT_STRING("Test: {:0}: {} : {}"), 42, "A", myVariable);
​    fmt::print(FMT_STRING("{}, {}, {}"), 42, "A", "B");

测试用例的输出(全部正确):

// Test non-namespace match.
​print(FMT_STRING("Hello, world!"));
​print(FMT_STRING("Test: {}"), 42);
​print(FMT_STRING("Test: {}: {}"), 42, "A");
​print(FMT_STRING("Test: {:0}: {} : {}"), 42, "A", myVariable);
​print(FMT_STRING("{}, {}, {}"), 42, "A", "B");
​    print(FMT_STRING("Hello, world!"));
​    print(FMT_STRING("Test: {}"), 42);
​    print(FMT_STRING("Test: {}: {}"), 42, "A");
​    print(FMT_STRING("Test: {:0}: {} : {}"), 42, "A", myVariable);
​    print(FMT_STRING("{}, {}, {}"), 42, "A", "B");
​
​// Test namespace match.
​fmt::print(FMT_STRING("Hello, world!"));
​fmt::print(FMT_STRING("Test: {}"), 42);
​fmt::print(FMT_STRING("Test: {}: {}"), 42, "A");
​fmt::print(FMT_STRING("Test: {:0}: {} : {}"), 42, "A", myVariable);
​fmt::print(FMT_STRING("{}, {}, {}"), 42, "A", "B");
​    fmt::print(FMT_STRING("Hello, world!"));
​    fmt::print(FMT_STRING("Test: {}"), 42);
​    fmt::print(FMT_STRING("Test: {}: {}"), 42, "A");
​    fmt::print(FMT_STRING("Test: {:0}: {} : {}"), 42, "A", myVariable);
​    fmt::print(FMT_STRING("{}, {}, {}"), 42, "A", "B");
​
​// Test compatibility with existing (should be no change).
​​fmt::print(FMT_STRING("Hello, world!"));
​​fmt::print(FMT_STRING("Test: {}"), 42);
​​fmt::print(FMT_STRING("Test: {}: {}"), 42, "A");
​​fmt::print(FMT_STRING("Test: {:0}: {} : {}"), 42, "A", myVariable);
​​fmt::print(FMT_STRING("{}, {}, {}"), 42, "A", "B");
​​    fmt::print(FMT_STRING("Hello, world!"));
​​    fmt::print(FMT_STRING("Test: {}"), 42);
​​    fmt::print(FMT_STRING("Test: {}: {}"), 42, "A");
​​    fmt::print(FMT_STRING("Test: {:0}: {} : {}"), 42, "A", myVariable);
​​    fmt::print(FMT_STRING("{}, {}, {}"), 42, "A", "B");

于 2020-07-04T10:44:13.570 回答