[英]{fmt} library: How to use RegEx to add compile time string checking?
I am using the {fmt} library.我正在使用{fmt}库。
Unfortunately, my program crashed after a few days, as I had an invalid format string.不幸的是,几天后我的程序崩溃了,因为我的格式字符串无效。 Easily fixed - but what if there are more?
很容易修复 - 但如果还有更多呢?
It is possible to do compile time checking of string formats , which would have caught this error:可以对字符串格式进行编译时检查,这会发现此错误:
// Replace this:
fmt::print("{}",42)
// With this:
fmt::print(FMT_STRING("{}"),42)
I could do this manually with about 500 print statements across the entire code base.我可以在整个代码库中使用大约 500 个打印语句手动执行此操作。
But I'm wondering - is there a way to do this with a RegEx and a find/replace in Visual Studio?但我想知道 - 有没有办法用正则表达式和 Visual Studio 中的查找/替换来做到这一点?
I got as far as using .NET RegEx tester and a string match on this:我尽可能使用.NET RegEx 测试器和一个字符串匹配:
print[(]".*".*[)];
However, a robust search-and-replace is still eluding me after many hours of trying.然而,经过数小时的尝试,我仍然无法实现强大的搜索和替换。
Used my answer below to solve the problem.使用我在下面的答案来解决问题。 Luckily enough, the rest were perfect.
幸运的是,rest 非常完美。
In VSCode, this works:在 VSCode 中,这有效:
Pattern: .*(fmt::print\()"\{\}"(,.*\)).*
模式:
.*(fmt::print\()"\{\}"(,.*\)).*
Replacement: $1FMT_STRING("{}")$2
替换:
$1FMT_STRING("{}")$2
If you're a Python fan, this also works if you read the C++ script in as a string:如果您是 Python 粉丝,那么如果您以字符串形式阅读 C++ 脚本,这也适用:
import re
pattern = '.*(fmt::print\()"{}"(.*\)).*'
fmt_snippet = 'fmt::print("{}",42)'
re.sub(pattern, r'\1FMT_STRING("{}")\2', fmt_snippet)
Expanding on the excellent answer from Mark Moretto:扩展 Mark Moretto 的出色回答:
In Visual Studio 2019, this worked beautifully across my entire C++ codebase:在 Visual Studio 2019 中,这在我的整个 C++ 代码库中运行良好:
Replace this:替换这个:
(.*print[(])(".*?")(.*[)];)
With this:有了这个:
$1FMT_STRING($2)$3
Then repeat with this to handle fmt::format
:然后重复这个来处理
fmt::format
:
(.*format[(])(".*?")(.*[)];)
For the global Search'n'Replace, ensure that RegEx
icon which is .*
is switched on.对于全局 Search'n'Replace,确保
.*
的RegEx
图标已打开。
I used .NET Regex Tester to form the expression.我使用.NET Regex Tester来形成表达式。
It correctly fixed up 500 of 505 instances in about 5 seconds, but it it did trip up on this sort of thing which I fixed manually:它在大约 5 秒内正确修复了 505 个实例中的 500 个,但它确实因我手动修复的这种事情而绊倒:
// Required manual fix (shift rogue bracket away from end).
auto y = format("Test=\"{}\""), 42);
I have always found RegEx expressions to be horribly complicated, but working through this example somehow made a lightbulb switch on in my head.我一直发现 RegEx 表达式非常复杂,但是通过这个示例,不知何故在我脑海中打开了一个灯泡开关。
(.*print[(])
matches anything from the start of the line to the opening print(
and replaces with $1
. (.*print[(])
匹配从行首到开头的任何内容print(
并替换为$1
。(".*?")
matches from the opening to closing quote, and replaces with $2
. (".*?")
匹配从开头到结尾的引号,并替换为$2
。(.*[)];)
matches everything to the closing );
(.*[)];)
匹配结束的所有内容);
, and replaces with $3
. $3
。$1FMT_STRING($2)$3
inserts FMT_STRING()
in the right place. $1FMT_STRING($2)$3
在正确的位置插入FMT_STRING()
。 Notes:笔记:
[(]
to indicate a literal (
.[(]
来指示文字(
..*
..*
。 This is a wildcard where .
.
means any character and *
means any number of repetitions.*
表示任意次数的重复。fmt::print(
. Needs a different RegEx to handle format(
and fmt::format(
(see above).fmt::print(
。需要不同的正则表达式来处理format(
和fmt::format(
(见上文)。?
?
to indicate that it should stop on the very first closing quote it sees (ie a "non-greedy match"). Paste the Input Tests Cases
below into .NET Regex Tester for syntax highlighting and easier editing of the expression to modify it slightly.将下面的
Input Tests Cases
粘贴到.NET 正则表达式测试器中,以便语法突出显示和更轻松地编辑表达式以稍微修改它。
Input test cases:输入测试用例:
// 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");
Output of test cases (all correct): Output 的测试用例(全部正确):
// 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");
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.