简体   繁体   English

{fmt} 库:如何使用 RegEx 添加编译时字符串检查?

[英]{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.然而,经过数小时的尝试,我仍然无法实现强大的搜索和替换。


Update 2020-07-04. 2020 年 7 月 4 日更新。

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); 

Explanation (Optional)说明(可选)

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 表达式非常复杂,但是通过这个示例,不知何故在我脑海中打开了一个灯泡开关。

  1. (.*print[(]) matches anything from the start of the line to the opening print( and replaces with $1 . (.*print[(])匹配从行首到开头的任何内容print(并替换为$1
  2. (".*?") matches from the opening to closing quote, and replaces with $2 . (".*?")匹配从开头到结尾的引号,并替换为$2
  3. (.*[)];) matches everything to the closing ); (.*[)];)匹配结束的所有内容); , and replaces with $3 . , 并替换为$3
  4. $1FMT_STRING($2)$3 inserts FMT_STRING() in the right place. $1FMT_STRING($2)$3在正确的位置插入FMT_STRING()

Notes:笔记:

  • On point 1:关于第 1 点:
    • Note the use of [(] to indicate a literal ( .请注意使用[(]来指示文字( .
    • Note the use of .* .注意使用.* This is a wildcard where .这是一个通配符 where . means any character and * means any number of repetitions.表示任意字符, *表示任意次数的重复。
    • It will also match on fmt::print( . Needs a different RegEx to handle format( and fmt::format( (see above).它还将匹配fmt::print( 。需要不同的正则表达式来处理format(fmt::format( (见上文)。
  • On point 2:关于第 2 点:
    • Note the use of ?注意使用? to indicate that it should stop on the very first closing quote it sees (ie a "non-greedy match").表示它应该在它看到的第一个结束引号处停止(即“非贪婪匹配”)。

Appendix A: Test Cases (Optional)附录 A:测试用例(可选)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM