将字节转换为UTF8编码的字符串是否安全?

3
今天我看到了一个包含以下代码的问题:
var accumulator = "";
var buffer = new byte[8192];
while (true)
{
    var readed = stream.Read(buffer, 0, buffer.Length);
    accumulator += Encoding.UTF8.GetString(buffer, 0, readed);
    if (readed < buffer.Length)
        break;
}
var result = Encoding.UTF8.GetBytes(accumulator);

我知道这段代码效率低,但它安全吗?是否有一些字节序列会破坏结果?


2
任何将码点分割在8192字节边界上的内容都会失败,是的。为什么要解码为UTF-8,然后立即重新编码呢? - Ry-
3
不,这不安全。更好的方法是 accumulator = new StreamReader(stream, Encoding.UTF8).ReadToEnd() - L.B
2个回答

7

代码明显有问题;如果这是作为答案提出的,那么你应该将错误带给作者的注意。

UTF-8序列可以超过一个字节,这是显而易见的。如果存在一个多字节序列,它从当前缓冲区的末尾开始并在下一个缓冲区的开头继续,则每个缓冲区到字符串的转换都将是错误的。


不好意思,这段代码其实是来自问题本身,而不是解答。从您的回答中,我意识到了该方法可能存在的漏洞。谢谢! - Aleks Andreev

2

安全的方法是使用有状态的UTF8解码器,可以从Encoding.UTF8.GetDecoder()获得。

有状态的解码器将内部保留与不完整的多字节序列对应的字节。下一次您提供更多字节时,它将完成序列并返回从序列解码的字符。

以下是如何使用它的示例。在我的实现中,我使用一个char[]缓冲区,其大小足以存储X个字节的完整转换。这样,我们只执行两个内存分配来读取整个流。

public static string ReadStringFromStream( Stream stream )
{
    // --- Byte-oriented state ---
    // A nice big buffer for us to use to read from the stream.
    byte[] byteBuffer = new byte[8192];

    // --- Char-oriented state ---
    // Gets a stateful UTF8 decoder that holds onto unused bytes when multi-byte sequences
    // are split across multiple byte buffers.
    var decoder = Encoding.UTF8.GetDecoder();

    // Initialize a char buffer, and make it large enough that it will be able to fit
    // a full reads-worth of data from the byte buffer without needing to be resized.
    char[] charBuffer = new char[Encoding.UTF8.GetMaxCharCount( byteBuffer.Length )];

    // --- Output ---
    StringBuilder stringBuilder = new StringBuilder();

    // --- Working state ---
    int bytesRead;
    int charsConverted;
    bool lastRead = false;

    do
    {
        // Read a chunk of bytes from our stream.
        bytesRead = stream.Read( byteBuffer, 0, byteBuffer.Length );

        // If we read 0 bytes, we hit the end of stream.
        // We're going to tell the converter to flush, and then we're going to stop.
        lastRead = ( bytesRead == 0 );

        // Convert the bytes into characters, flushing if this is our last conversion.
        charsConverted = decoder.GetChars( 
            byteBuffer, 
            0, 
            bytesRead, 
            charBuffer, 
            0, 
            lastRead 
        );

        // Build up a string in a character buffer.
        stringBuilder.Append( charBuffer, 0, charsConverted );
    }
    while( lastRead == false );

    return stringBuilder.ToString();
}

1
不需要重新发明轮子(假设它正在工作),请参见“L.B”的评论。 - EZI
@EZI - 当然可以,但这说明了如何自己完成,因此,如果您不想一直阅读流程或有其他不同的要求,它会给您一些可以适应您情况的东西。偶尔拉开帷幕也没什么不对的。 - antiduh

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