【问题标题】:Generating random string valid for UTF-8 encode and decode生成对 UTF-8 编码和解码有效的随机字符串
【发布时间】:2013-06-06 23:10:53
【问题描述】:

出于测试目的,我需要生成一个随机字符串,然后将其编码为字节数组以通过 Web 传输并解码回结果字符串。该测试使用 NUnit 框架将原始字符串与结果字符串进行比较。由于编码后的字节数组必须对 Web 友好,所以使用 UTF-8 编码。

字符串被Encoder.GetBytesUTF8Encoding编码成一个字节数组。字节数组被Decoder.GetCharsUTF8Encoding解码为字符串。

原始字符串需要随机生成,包含任意字符序列,可以使用UTF-8编码进行编码/解码。

我第一次尝试生成字符串是:

   public static String RandomString(Random rnd, Int32 length) {
          StringBuilder str = new StringBuilder(length);
          for (int i = 0; i < length; i++)
                 str.Append((char)rnd.Next(char.MinValue, char.MaxValue));
          return str.ToString();
   }

上面的代码产生了带有无效序列的字符串来编码。

在网上找了一些建议,对代码进行了改进:

   public static String RandomString(Random rnd, Int32 length) {
          StringBuilder str = new StringBuilder(length);
          for (int i = 0; i < length; i++) {
                 char c = (char)rnd.Next(char.MinValue, char.MaxValue);
                 while (c >= 0xD800 && c <= 0xDFFF)
                        c = (char)rnd.Next(char.MinValue, char.MaxValue);
                 str.Append(c);
          return str.ToString();
   }

以上代码编码没有问题,但是解码字节数组失败。此外,我不确定代码是否可以涵盖所有可能的情况。

任何建议,如何在 C# 中生成具有给定要求的随机字符串。

UPD:在编码/解码中使用随机字符串:

   public static Encoder Utf8Encode = new UTF8Encoding(false, true).GetEncoder();
   public static Decoder Utf8Decode = new UTF8Encoding(false, true).GetDecoder();

   public unsafe void TestString(Random rnd, int length, byte* byteArray, 
                 int arrayLenght) {
          int encodedLen;
          String str = RandomString(rnd, length);
          fixed (char* pStr = str) {
                 encodedLen = Utf8Encode.GetBytes(pStr, str.Length, byteArray,
                        arrayLenght, true);
          }
          char* buffer = stackalloc char[8192];
          int decodedLen = Utf8Decode.GetChars(byteArray, encodedLen, buffer, 
                 8192, true);
          String res = new String(buffer, 0, decodedLen);
          Assert.AreEqual(str, res);
   }

【问题讨论】:

  • 我不认为随机字符串是单元测试的好方法,理论上它违反了原则Repeatable: Tests should produce the same results each time.. every time. Tests should not rely on uncontrollable params。我认为随机测试来自于为边缘情况创建实际测试的懒惰。
  • 随机生成器始终使用相同的密封值创建,因此每次测试运行都会产生相同的结果,即测试是可重复的。
  • 如果它是一个有效的 Unicode 字符串,你最好只使用 U+0000 和 U+D7FF 之间的值,因为在 U+E000 之上还有其他非法值。无论如何,你说“解码字节数组”。你能显示字节数组吗?您的示例中只有 UTF-16 字符串。
  • 对于任何 Unicode 测试,肯定需要高于 U+FFFF 的值。如果只是随机噪声,则将每个字符创建为 0 到 10FFFF 之间的随机元素,并拒绝代理和非字符。 unicode.org/faq/private_use.html#nonchar4 列出了后者的全部 66 个。然后转换为 UTF-8。
  • 排除 0xE000 以上的值有助于解决解码失败的问题。

标签: c# string random utf-8 encode


【解决方案1】:

我使用下面的代码生成随机 UTF-8 字符字节序列。我不能保证它包含 UTF-8 规范的任何方面,但它对我的测试目的很有价值,所以我将它发布在这里。

private static readonly (int, int)[] HeadByteDefinitions =
{
    (1 << 7, 0b0000_0000),
    (1 << 5, 0b1100_0000),
    (1 << 4, 0b1110_0000),
    (1 << 3, 0b1111_0000)
};

static byte[] RandomUtf8Char(Random gen)
{
    const int totalNumberOfUtf8Chars = (1 << 7) + (1 << 11) + (1 << 16) + (1 << 21);
    int tailByteCnt;
    var rnd = gen.Next(totalNumberOfUtf8Chars);
    if (rnd < (1 << 7))
        tailByteCnt = 0;
    else if (rnd < (1 << 7) + (1 << 11))
        tailByteCnt = 1;
    else if (rnd < (1 << 7) + (1 << 11) + (1 << 16))
        tailByteCnt = 2;
    else
        tailByteCnt = 3;

    var (range, offset) = HeadByteDefinitions[tailByteCnt];
    var headByte = Convert.ToByte(gen.Next(range) + offset);
    var tailBytes = Enumerable.Range(0, tailByteCnt)
        .Select(_ => Convert.ToByte(gen.Next(1 << 6) + 0b1000_0000));

    return new[] {headByte}.Concat(tailBytes).ToArray();
}

【讨论】:

    猜你喜欢
    • 2018-11-05
    • 1970-01-01
    • 2021-03-13
    • 2013-05-11
    • 2010-12-01
    • 2011-02-17
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    相关资源
    最近更新 更多