如何在.NET中生成UTF-8字符集中的所有字符

17

我被分配任务,需要生成UTF-8字符集中的所有字符来测试系统如何处理它们。我在字符编码方面没有太多经验。我打算尝试的方法是递增一个计数器,然后尝试将该十进制数转换为其对应的UTF-8字符,但迄今为止我没有找到在C# 3.5中实现这一目标的有效方法。

非常感谢任何建议。


1
Unicode标准中有1,112,064个代码点。你真的想生成所有这些吗? - Daniel Pryden
1
感谢您的反馈,这使事情变得更清晰了。为了更好地说明我的任务,我正在测试一个服务,似乎无法处理一些中文字符。因此,我们决定应该能够测试所有外语字符,因为系统需要能够处理各种国际语言。我正在寻找一个简单而有效的解决方案,不会花费太长时间,并具有最大的覆盖范围。 - FireWire
@DanielPryden,对于计算机来说,1,112,064并不是一个很大的数字。如果需要100个周期生成一个数字,那么一台3GHz的计算机只需要0.037秒就可以完成全部计算。 - kristianp
10个回答

8
System.Net.WebClient client = new System.Net.WebClient();
string definedCodePoints = client.DownloadString(
                         "http://unicode.org/Public/UNIDATA/UnicodeData.txt");
System.IO.StringReader reader = new System.IO.StringReader(definedCodePoints);
System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
while(true) {
  string line = reader.ReadLine();
  if(line == null) break;
  int codePoint = Convert.ToInt32(line.Substring(0, line.IndexOf(";")), 16);
  if(codePoint >= 0xD800 && codePoint <= 0xDFFF) {
    //surrogate boundary; not valid codePoint, but listed in the document
  } else {
    string utf16 = char.ConvertFromUtf32(codePoint);
    byte[] utf8 = encoder.GetBytes(utf16);
    //TODO: something with the UTF-8-encoded character
  }
}

以上代码应按当前分配的Unicode字符进行迭代。您可能需要在本地解析UnicodeData文件并纠正我所犯的任何C#错误。

当前分配的Unicode字符集小于可以定义的字符集。当您打印其中之一时,是否看到该字符取决于许多其他因素,如字体和它通过的其他应用程序,直到发送到您的眼球。


7

“UTF-8字符”这个说法并不准确。您是指Unicode字符还是Unicode字符的UTF-8编码?

将int转换为Unicode字符很容易,前提是存在该代码的映射:

char c = (char)theNumber;

如果您想要该字符的UTF-8编码,也不是很难:
byte[] encoded = Encoding.UTF8.GetBytes(c.ToString())

您需要查看Unicode标准,以了解定义Unicode字符的数字范围。


5

即使您生成了所有字符,您会发现这不是一种有效的测试。其中一些字符是组合标记,这意味着它们将与其后的下一个字符结合 - 拥有一串组合标记的字符串将没有太多意义。还有其他特殊情况。最好使用您需要支持的语言中的实际文本。


2
您可以使用暴力破解方法来确定编码支持哪些代码点。要这样做,只需遍历所有可能的代码点,将它们转换为字符串,然后查看是否会抛出异常Encoding.GetBytes()(在将Encoding.EncoderFallback设置为EncoderExceptionFallback之后)。
IEnumerable<int> GetAllWritableCodepoints(Encoding encoding)
{
    encoding = Encoding.GetEncoding(encoding.WebName, new EncoderExceptionFallback(), new DecoderExceptionFallback());

    var i = -1;
    // Docs for char.ConvertFromUtf32() say that 0x10ffff is the maximum code point value.
    while (i != 0x10ffff)
    {
        i++;

        var success = false;
        try
        {
            encoding.GetByteCount(char.ConvertFromUtf32(i));
            success = true;
        }
        catch (ArgumentException)
        {
        }
        if (success)
        {
            yield return i;
        }
    }
}

这个方法应该支持在 .net 中发现由代理对表示的字符。然而,它非常缓慢(在我的机器上运行需要几分钟),可能不切实际。


2
UTF-8并不是一个字符集,而是一种能够将Unicode字符集中的任何字符编码为二进制数据的字符编码。如果您需要处理基本多语言平面之外(即U + FFFF以上)的字符,则可能会变得稍微棘手。请问您需要更多信息吗?您可以对所有可能的Unicode字符进行编码(包括目前未分配的字符)。

1

UTF-8不是字符集,它是一种编码方式。任何Unicode值都可以使用不同的字节长度进行UTF-8编码。

对于.NET来说,字符是16位的(虽然不是完整的Unicode集合,但这是最实用的),因此您可以尝试以下方法:

 for (char i = 0; i < 65536; i++) {
     string s = "" + i;
     byte[] bytes = Encoding.UTF8.GetBytes(s);
     // do something with bytes
 }

3
你的代码是正确的,但是你的第二段内容有误导性。System.Char 是一个16位的值,没错。但是MSDN明确表示 System.Char 是一个UTF-16编码点,也就意味着它并不是严格意义上的字符。在UTF-8中有很多Unicode字符的编码点超过了65536。你说“这不是完整的Unicode集合,但它是最实用的” - 我不确定这是正确的,并且这绝对不是避免测试U+FFFF以上代码点的好理由。 - Daniel Pryden

1

这将为您提供字符集中的所有字符 - 只需确保在指定编码时指定字符集:

var results = new ConcurrentBag<int> ();
Parallel.For (0, 10, set => {
    var encoding = Encoding.GetEncoding ("ISO-8859-1");
    var c = encoding.GetEncoder ();
    c.Fallback = new EncoderExceptionFallback ();
    var start = set * 1000;
    var end = start + 1000;
    Console.WriteLine ("Worker #{0}: {1} - {2}", set, start, end);

    char[] input = new char[1];
    byte[] output = new byte[5];
    for (int i = start; i < end; i++) {
        try {
            input[0] = (char)i;
            c.GetBytes (input, 0, 1, output, 0, true);
            results.Add (i);
        }
        catch {
        }
    }
});
var hashSet = new HashSet<int> (results);
//hashSet.Remove ((int)'\r');
//hashSet.Remove ((int)'\n');
var sorted = hashSet.ToArray ();
Array.Sort (sorted);
var charset = new string (sorted.Select (i => (char)i).ToArray ());

1
这段代码将会产生一个文件输出。所有可打印和不可打印的字符都会被包含在其中。
Encoding enc = (Encoding)Encoding.GetEncoding("utf-8").Clone();
enc.EncoderFallback = new EncoderReplacementFallback("");
char[] chars = new char[1];
byte[] bytes = new byte[16];

using (StreamWriter sw = new StreamWriter(@"C:\utf-8.txt"))
{
    for (int i = 0; i <= char.MaxValue; i++)
    {
        chars[0] = (char)i;
        int count = enc.GetBytes(chars, 0, 1, bytes, 0);

        if (count != 0)
        {
            sw.WriteLine(chars[0]);
        }
    }
}

太棒了。我甚至不需要打开VS。我用一个脚本编译了代码。它立刻,马上,而且准确地开始工作,几乎就像直接"开箱即用"一样。 - Garric

0

0

使用PowerShell代码,我将Jake建议的代码组合成256行长度的文本文件。

服务符号会创建两个在原始文件中不存在的空行,在进行PowerShell处理之前必须从原始文本文件中删除,以便正确创建结果文件。

这里只是展示ASC2部分应该是什么样子。

NUL SOH STX ETX EOT ENQ ACK BEL BS TAB LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US 空格 ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ PAD HOP BPH NBH IND NEL SSA ESA HTS HTJ VTS PLD PLU RI SS2 SS3 DCS PU1 PU2 STS CCH MW SPA EPA SOS SGCI SCI CSI ST OSC PM APC 不间断空格 ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ

在初始文件中,每个字符都将位于新行上。

最好使用Notepad ++查看服务符号。最好用手替换它们为文本。

还包含两个服务符号,一个在asc2部分下方,另一个在结尾处 - 很多。

但是,要欣赏彩色表情符号,您可以将喜欢的文本简单地复制到Word或社交网络中。Word比记事本更好地解释字符,但比网站差。

$arrayFromFile = [IO.File]::ReadAllLines('C:\utf-8.txt')
$counter = [pscustomobject] @{ Value = 0 }
$groupSize = 256
$text=''
$groups = $arrayFromFile | Group-Object -Property { [math]::Floor($counter.Value++ / $groupSize) }
foreach ($group in $groups){
    $text+=$group.Group -join (' ')
    $text+="`n"
}
$text | Out-File -FilePath 'C:\utf-8 (sorted).txt'

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