{fmt} 库:如何使用 RegEx 添加编译时字符串检查?
{fmt} library: How to use RegEx to add compile time string checking?
我正在使用 {fmt} 库。
不幸的是,几天后我的程序崩溃了,因为我的格式字符串无效。很容易修复 - 但如果还有更多怎么办?
可以执行 compile time checking of string formats,这会捕获此错误:
// Replace this:
fmt::print("{}",42)
// With this:
fmt::print(FMT_STRING("{}"),42)
我可以在整个代码库中使用大约 500 个打印语句手动执行此操作。
但我想知道 - 有没有办法用正则表达式和 Visual Studio 中的 find/replace 来做到这一点?
我使用 .NET RegEx tester 和字符串匹配:
print[(]".*".*[)];
但是,经过数小时的尝试,我仍然无法找到可靠的搜索和替换。
更新 2020-07-04。
使用我下面的答案解决了问题。幸运的是,其余的都很完美。
在 VSCode 中有效:
模式:.*(fmt::print\()"\{\}"(,.*\)).*
替换:FMT_STRING("{}")
如果您是 Python 粉丝,如果您以字符串形式读取 C++ 脚本,这也适用:
import re
pattern = '.*(fmt::print\()"{}"(.*\)).*'
fmt_snippet = 'fmt::print("{}",42)'
re.sub(pattern, r'FMT_STRING("{}")', fmt_snippet)
扩展 Mark Moretto 的出色回答:
在 Visual Studio 2019 年,这在我的整个 C++ 代码库中运行良好:
替换为:
(.*print[(])(".*?")(.*[)];)
有了这个:
FMT_STRING()
然后重复此处理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 表达式非常复杂,但是通过这个例子不知何故让我的脑袋亮了起来。
(.*print[(])
匹配从行首到开头 print(
的任何内容,并替换为 </code>.</li>
<li><code>(".*?")
匹配从开盘到收盘的报价,并替换为 </code>.</li>
<li><code>(.*[)];)
匹配结束 );
的所有内容,并替换为 </code>.</li>
<li><code>FMT_STRING()
在正确的位置插入 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");
我正在使用 {fmt} 库。
不幸的是,几天后我的程序崩溃了,因为我的格式字符串无效。很容易修复 - 但如果还有更多怎么办?
可以执行 compile time checking of string formats,这会捕获此错误:
// Replace this:
fmt::print("{}",42)
// With this:
fmt::print(FMT_STRING("{}"),42)
我可以在整个代码库中使用大约 500 个打印语句手动执行此操作。
但我想知道 - 有没有办法用正则表达式和 Visual Studio 中的 find/replace 来做到这一点?
我使用 .NET RegEx tester 和字符串匹配:
print[(]".*".*[)];
但是,经过数小时的尝试,我仍然无法找到可靠的搜索和替换。
更新 2020-07-04。
使用我下面的答案解决了问题。幸运的是,其余的都很完美。
在 VSCode 中有效:
模式:.*(fmt::print\()"\{\}"(,.*\)).*
替换:FMT_STRING("{}")
如果您是 Python 粉丝,如果您以字符串形式读取 C++ 脚本,这也适用:
import re
pattern = '.*(fmt::print\()"{}"(.*\)).*'
fmt_snippet = 'fmt::print("{}",42)'
re.sub(pattern, r'FMT_STRING("{}")', fmt_snippet)
扩展 Mark Moretto 的出色回答:
在 Visual Studio 2019 年,这在我的整个 C++ 代码库中运行良好:
替换为:
(.*print[(])(".*?")(.*[)];)
有了这个:
FMT_STRING()
然后重复此处理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 表达式非常复杂,但是通过这个例子不知何故让我的脑袋亮了起来。
(.*print[(])
匹配从行首到开头print(
的任何内容,并替换为</code>.</li> <li><code>(".*?")
匹配从开盘到收盘的报价,并替换为</code>.</li> <li><code>(.*[)];)
匹配结束);
的所有内容,并替换为</code>.</li> <li><code>FMT_STRING()
在正确的位置插入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");