C# 中的 printf 实现






4.41/5 (46投票s)
本文描述了使用 C# 实现 printf 的方法。
引言
在进行一个将大型 C/C++ Unix 应用程序移植到 .NET 世界的项目时,语言要求从混合的 C#/VB.NET/Managed C++ 更改为 C#(并且只有 C#)。 在该项目的 C/C++ 源代码中,有很多 [sf]printf
语句。 将这些语句迁移到相应的 C# String.Format
格式不仅令人讨厌,而且还有点问题。 这是因为 String.Format
不支持所有必需的可能性,就像 printf
那样。 那么,该怎么办呢? 解决方案是在 C# 中实现一个等效于 printf
的功能,这就是我将在本文中向您展示的内容。
使用代码
放置对演示项目的 Tools 程序集的引用,或者将我的代码合并到您的源代码中。 使用适当的参数调用 Tools.printf
,您就完成了。 printf
具有很多转换和格式化选项 -- 比 String.Format
更多 -- 但这也带来了更高的复杂性,如果您从未使用过 printf
的话。 要了解有关 printf
及其可能性的更多信息,请查看 printf 手册页 或者直接在 Google 上搜索 man printf。
重要的是要注意,目前并非支持所有可能的 printf
选项、转换和格式。 网上也有不止一个 printf
实现! 此实现支持最常见的选项。 它支持 l 和 h 长度修饰符,所有标志(例如 #+- ')和以下格式:iudxXfFeEgGon。 ASCII 和 Unicode 字符串和字符之间没有区别;它们始终是 Unicode。
printf
输出到控制台。 它还有诸如 sprinf
之类的变体,它返回格式化的字符串,而 fprinf
将格式化的输出写入流。 它支持可变长度参数。 如果格式占位符多于实际值参数,则没有值的占位符将从结果中删除。
// Ouput: Account balance: +12.345.678,00 (great)
Tools.printf("Account balance: %'+20.2f (%s)\n", 12345678, "great");
构建和测试演示项目
演示项目使用 NUnit 及其测试用例来测试一些 printf
功能,但不是所有这些功能的组合。 因此,您需要安装 NUnit。
关注点
现在,将具有大量 [sf]printf
语句的 C/C++ 代码转换为 C# 变得容易一些。 此 printf
实现使用 Regex \%([\'\#\-\+ ]*)(\d*)(?:\.(\d+))?([hl])?([dioxXucsfeEgGpn%])
来解析格式字符串,并在可能的情况下在内部使用 String.Format
。 仅在其他方式不可行时才手动处理字符串,因此代码非常简单且小巧。
有些人指出可以使用底层的非托管 Windows API 函数,例如
[DllImport("msvcrt.dll", SetLastError = false,
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int sprintf(StringBuilder buff, string format, __arglist);
...
StringBuilder sb = new StringBuilder(256);
DateTime dt = DateTime.Now;
int hr = dt.Hour;
int mn = dt.Minute;
int sc = dt.Second;
int ml = dt.Millisecond;
MSVCRT.sprintf(sb, "%02i:%02i:%02i.%03i", __arglist(hr, mn, sc, ml));
string s = sb.ToString();
...
或者从 C# 调用的托管 C++ 包装器
namespace FormatHelper
{
void FormatHelper::FormatDouble(String ^%sResult, String ^sFormat, double fVal)
{
CString sStr;
sStr.Format (CString(sFormat), fVal);
sResult = gcnew String(sStr);
}
}
像这样从 C# 调用它
using FormatHelper;
string s = string.Empty;
Class1.FormatDouble(ref s, "%f6.1",132.459);
两种解决方案都有效,但有时由于各种原因(管理 - 你懂的?;-))无法使用非托管代码和其他语言。 这就是因为这是 C# 中 printf
的完整重写,没有任何依赖项。
更新
感谢 Rainer Helbing,此 printf
实现现在还支持 "%[parameterIndex][flags][width][.precision][length]type" 形式的参数索引。 可以在这里找到详细描述。
NUnit 测试例程也已修改,以在所有国家/地区设置下使用十进制和组分隔符按预期工作。
解决了指定十六进制格式精度时的一个错误。
历史
- 2007.06.14 -- 首次发布。
- 2009.01.26 -- 更新。