在.NET中如何检测非可打印字符?

50

我想知道在 .NET 2.0 中是否有一种方法可以检查一个字符是否可打印 - 就像标准 C 中的 isprint(int)函数。

我找到了Char.IsControl(Char)

这个函数可以用于这个目的吗?


请定义您所说的“可打印”。 “Coöperate” 可以打印吗? “協力する”可以打印吗? - Dour High Arch
3
既然我的屏幕上显示了umlaut和中文字符,那我当然认为它们是“可打印的”——当然! - Baldewin
那么您应该取消勾选JWL_的答案,因为它说两者都不是“可打印的”。 - Dour High Arch
这确实需要更好的定义。即使我们认为控制字符为不可打印字符,空格字符为可打印字符,那么零宽空格是可打印还是不可打印呢? - Timo
4个回答

45

您可能需要使用Char.IsControl(Char),这就是我正在使用的方法。绝对不要使用<0x20的方法,因为任何非拉丁文字符和大多数非英语字符都将高于127。


9
除了控制字符外,您可能还希望排除 0x20280x2029。尽管它们没有被分类为控制字符,但许多系统也认为它们是不可打印的。这些字符可以分别通过 UnicodeCategory.LineSeparatorUnicodeCategory.ParagraphSeparator 进行过滤。 - MattDavey
3
这将删除回车和换行符。不要使用它来检查可打印字符,否则它会删除文本中的所有换行符。 - Ayrton Massey

25
如果您所说的“可打印”是指呈现了“某些东西”,即使这个“某些东西”是空白(空格),仅使用[negating] Char.IsControl()是不足以确定一个字符是否可打印的。
即使在单字节U + 0000-U + 00FF Unicode范围内(与ASCII / ISO-8859-1兼容),它也不够,因为除空格字符之外的ASCII空白字符也被归类为控制字符,因此Char.IsControl('\t')和Char.IsControl('\n')也会返回true。
在单字节范围之外,还有其他非呈现字符的类别必须被识别。
单字节U + 0000-U + 00FF Unicode范围的解决方案(与ASCII / ISO-8859-1兼容):
  // Sample input char.
  char c = (char)0x20; // space

  var isPrintable = ! Char.IsControl(c) || Char.IsWhiteSpace(c);
所有 Unicode 字符近似解决方案
可悲的是,并没有简单的完整解决方案:
- 基于 Char 的测试的一个根本限制是,类型 Char 只能表示编码点为 U+FFFF 或更低的字符,即仅限基本多文种平面(BMP)中的字符。高于 BMP 的字符 - 具有更高编码点的字符 - 必须表示为 两个 Char 实例(称为代理对)。
- UnicodeCategory.PrivateUse 字符类别不是标准化的,正如名称所示,在 macOS 上,U+F8FF 包含了 Apple 符号,而在 Windows 上它未定义。因此,它可能包含可打印的字符,需要动态确定它们是否可打印。
- UnicodeCategory.Format 类别主要包含非渲染字符,但也有例外情况 - 参见此表
- 对于给定版本的 Unicode 标准,可以为这些异常情况进行硬编码,但这很麻烦,而且随着时间的推移可能会过时。
因此,以下代码假定 UnicodeCategory.PrivateUseUnicodeCategory.Format 中的所有字符都是可打印的,这意味着至少有一些字符将被错误分类。
using System;
using System.Linq;
using System.Globalization;

// ...

  // Sample input char.
  char c = (char)0x20; // space

  // The set of Unicode character categories containing non-rendering,
  // unknown, or incomplete characters.
  // !! Unicode.Format and Unicode.PrivateUse can NOT be included in
  // !! this set, because they may (private-use) or do (format)
  // !! contain at least *some* rendering characters.
  var nonRenderingCategories = new UnicodeCategory[] {
    UnicodeCategory.Control,
    UnicodeCategory.OtherNotAssigned,
    UnicodeCategory.Surrogate };

  // Char.IsWhiteSpace() includes the ASCII whitespace characters that
  // are categorized as control characters. Any other character is
  // printable, unless it falls into the non-rendering categories.
  var isPrintable = Char.IsWhiteSpace(c) ||
    ! nonRenderingCategories.Contains(Char.GetUnicodeCategory(c));

3
我已经使用C#编写了15年以上,直到今天我才意识到 Char.IsControl('\t') == falseTIL,谢谢!(现在我不敢想象因为这个假设而无意中引入到世界上所有微妙的错误)。 - Dai
@Dai: :) 很高兴听到答案有帮助;是的,(Unicode)字符的世界充满了奇妙和陷阱。 - mklement0

7
除了Char.IsControlChar()之外,还有几个函数可以用来确定给定的字符值属于哪个类别:
  • IsLetter()
  • IsNumber()
  • IsDigit()
  • IsLetterOrDigit()
  • IsSymbol()
  • IsPunctuation()
  • IsSeparator()
  • IsWhiteSpace()
如果你拥有的是“传统ASCII文本”文件,并且想要使用提供的函数,那么表达式为:
(Char.IsLetterOrDigit(ch) || Char.IsPunctuation(ch) || Char.IsSymbol(ch) || (ch==' '))

应该可以工作。

现在,如果你正在使用Unicode,那么你会遇到麻烦。即使在过去,空格是否可打印是有争议的(因此有isprint()isgraph()函数)。请参见与“可打印”Unicode字符相关的问题和答案


-12
private bool IsPrintableCharacter(char candidate)
{
    return !(candidate < 0x20 || candidate > 127);
}

为什么这个问题和JWL_的回答包括字符127,根据维基百科,它是DEL?链接:http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters - Paul L
3
许多可打印的Unicode字符,包括五个罗马尼亚字母,不在32...127范围内。 - Călin Darie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接