好的,如果您需要用C语言进行编程,请提供具体的问题或任务,我会尽力帮助您解答或编写相应的代码。
摘要:前言 什么?用 C# 插值字符串处理器写一个输入用的 sscanf?你确定不是输出用的 sprintf? 我猜不少读者看到标题后大概会有上述的想法。然而我们这里还真就是实现 sscanf,而不是 sprintf。 插值字符串处理器 C# 有
前言
什么?用 C# 插值字符串处理器写一个输入用的 sscanf?你确定不是输出用的 sprintf?
我猜不少读者看到标题后大概会有上述的想法。然而我们这里还真就是实现 sscanf,而不是 sprintf。
插值字符串处理器
C# 有一个特性叫做插值字符串,使用插值字符串,你可以自然地往字符串里面插入变量的值,比如:$"abc{x}def",这一改以往通过 string.Format 来格式化字符串的方式,使得不再需要先传递一个字符串模板再挨个传递参数,非常方便。
在插值字符串的基础上更进一步,C# 支持插值字符串处理器,意味着你可以自定义字符串的插值行为。比如一个简单的例子:
[InterpolatedStringHandler]
struct Handler(int literalLength, int formattedCount)
{
public void AppendLiteral(string s)
{
Console.WriteLine($"Literal: '{s}'");
}
public void AppendFormatted<T>(T v)
{
Console.WriteLine($"Value: '{v}'");
}
}
在使用的时候,只需要把传递 string 参数的地方都换成这个 Handler 类型,就能做到按照你自定义的方式来处理插值字符串,我们的插值字符串会被 C# 编译器自动变换成 Handler 的构造和调用然后被传入:
void Foo(Handler handler) { }
var x = 42;
Foo($"abc{x}def");
比如上面这个例子,你会得到输出:
Literal: 'abc'
Value: '42'
Literal: 'def'
这大大方便了各种结构化日志框架的处理,你只需要简单的把插值字符串传递进去,日志框架就能根据你插值的方式来做到结构化解析,从而完全避免了手动去格式化字符串。
带参数的插值字符串处理器
其实 C# 的插值字符串处理器还支持带额外的参数:
[InterpolatedStringHandler]
struct Handler(int literalLength, int formattedCount, int value)
{
public void AppendLiteral(string s)
{
Console.WriteLine($"Literal: '{s}'");
}
public void AppendFormatted<T>(T v)
{
Console.WriteLine($"Value: '{v}'");
}
}
void Foo(int value, [InterpolatedStringHandlerArgument("value")] Handler handler) { }
Foo(42, $"abc{x}def");
这么一来,42 就会被传入 handler 的 value 参数当中,这允许我们捕获来自调用方的上下文,毕竟在日志场景中,根据不同参数来决定不同的格式很常见。
sscanf?
众所周知 C/C++ 里面有一个很常用的函数 sscanf,它接受一个文本输入和一个格式化模板,然后再传递对格式化部分的变量的引用,就能把变量的值解析出来:
const char* input = "test 123 test";
const char* template = "test %d test";
int v = 0;
sscanf(input, template, &v);
printf("%d\n", v); // 123
那我们能不能在 C# 里复刻一个呢?当然可以!只不过需要一点点黑魔法。
