0

我正在实现一个TryParse()ASCII 字符串类的方法。该方法接受一个字符串并将其转换为 C 风格的字符串(即以空字符结尾的 ASCII 字符串)。

我一直只使用 a Parse(),使用:: 转换为 ASCII

public static bool Parse(string s, out byte[] result)
{
    result = null;
    if (s == null || s.Length < 1)
        return false;

    byte[]d = new byte[s.Length + 1]; // Add space for null-terminator
    System.Text.Encoding.ASCII.GetBytes(s).CopyTo(d, 0); 
    // GetBytes can throw exceptions 
    // (so can CopyTo() but I can replace that with a loop)
    result = d;
    return true;
}

但是,由于 TryParse 的想法的一部分是消除异常的开销并GetBytes()引发异常,所以我正在寻找一种不同的方法,它不会这样做。

也许有一个类似TryGetbytes()的方法?

或者,也许我们可以推断标准 .Net 的预期格式string并以数学方式执行更改(我对 UTF 编码不太熟悉)?

编辑:我猜对于字符串中的非 ASCII 字符,该TryParse()方法应该返回false

编辑:我希望当我开始ToString()为这个类实现方法时,我可能需要在那里做相反的事情。

4

3 回答 3

2

Encoding.GetBytes根据文档,可能会抛出两种可能的异常。

ArgumentNullException很容易避免。对您的输入进行空检查,您可以确保它永远不会被抛出。

EncoderFallbackException需要更多调查......阅读文档:

回退策略确定编码器如何处理无效字符或解码器如何处理无效字节。

如果我们查看ASCII 编码的文档,我们会看到:

它使用替换回退来用问号(“?”)字符替换无法编码的每个字符串和无法解码的每个字节。

这意味着它不使用异常回退,因此永远不会抛出EncoderFallbackException.

因此,总而言之,如果您使用 ASCII 编码并确保不传入空字符串,那么您将永远不会因调用GetBytes.

于 2017-07-21T11:52:17.780 回答
2

两种选择:

您可以完全忽略Encoding,并自己编写循环:

public static bool TryParse(string s, out byte[] result)
{
    result = null;
    // TODO: It's not clear why you don't want to be able to convert an empty string
    if (s == null || s.Length < 1)
    {
        return false;
    }

    byte buffer = new byte[s.Length + 1]; // Add space for null-terminator
    for (int i = 0; i < s.Length; i++)
    {
        char c = s[i];
        if (c > 127)
        {
            return false;
        }
        buffer[i] = (byte) c;
    }
    result = buffer;
    return true;
}

这很简单,但可能比使用Encoding.GetBytes.

第二种选择是使用自定义EncoderFallback

public static bool TryParse(string s, out byte[] result)
{
    result = null;
    // TODO: It's not clear why you don't want to be able to convert an empty string
    if (s == null || s.Length < 1)
    {
        return false;
    }

    var fallback = new CustomFallback();
    var encoding = new ASCIIEncoding { EncoderFallback = fallback };
    byte buffer = new byte[s.Length + 1]; // Add space for null-terminator
    // Use overload of Encoding.GetBytes that writes straight into the buffer
    encoding.GetBytes(s, 0, s.Length, buffer, 0);
    if (fallback.HadErrors)
    {
        return false;
    }
    result = buffer;
    return true;
}

不过,这需要编写CustomFallback——它需要基本上跟踪它是否曾被要求处理无效输入。

如果您不介意编码处理数据两次,您可以Encoding.GetByteCount使用基于 UTF-8 的编码和替换回退(使用非 ASCII 替换字符)调用,并检查是否返回与字符串中的字符数。如果是,请致电Encoding.ASCII.GetBytes.

就个人而言,除非您有理由相信它太慢,否则我会选择第一个选项。

于 2017-07-21T11:54:56.903 回答
1

GetBytes方法正在引发异常,因为您Encoding.EncoderFallback指定它应该引发异常。

创建一个编码对象EncoderReplacementFallback以避免不可编码字符的异常。

Encoding encodingWithFallback = new ASCIIEncoding() { DecoderFallback = DecoderFallback.ReplacementFallback };
encodingWithFallback.GetBytes("Hɘ££o wor£d!");

这种方式模仿TryParse原始 .NET 值类型的方法:

bool TryEncodingToASCII(string s, out byte[] result)
{
    if (s == null || Regex.IsMatch(s, "[^\x00-\x7F]")) // If a single ASCII character is found, return false.
    {
        result = null;
        return false;
    }
    result = Encoding.ASCII.GetBytes(s); // Convert the string to ASCII bytes.
    return true;
}
于 2017-07-21T11:28:44.750 回答