65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 中的 printf 实现

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.41/5 (46投票s)

2007年6月20日

MIT

3分钟阅读

viewsIcon

154951

downloadIcon

2414

本文描述了使用 C# 实现 printf 的方法。

Screenshot - printf.jpg

引言

在进行一个将大型 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 实现! 此实现支持最常见的选项。 它支持 lh 长度修饰符,所有标志(例如 #+- ')和以下格式: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 -- 更新。
© . All rights reserved.