如何使C#抛出解码异常?

3 投票
1 回答
2277 浏览
提问于 2025-04-18 14:23

我想让我的C#应用程序(有图形界面)帮助用户在“unicode (utf-8)”和“legacy (cp1252)”之间选择。我希望给用户提供两个独立的真/假选项,告诉他们文件是否可以在这两种格式下“成功”读取(虽然不一定是正确的),并且没有任何细节丢失。

我在C#中尝试了以下代码,但没有成功。也就是说,无论我用它去读取一个我知道包含非罗马字符的utf-8文本文件,它似乎总是返回true。

[编辑:其实,我不应该认为这个会失败。可能这是一个合理的成功,但结果却不正确,因为大多数(所有?)字节流在cp1252编码下也是有效的。测试反向读取时,确实能找到无效的utf-8,就像下面的Python代码那样。]

例如,CanBeReadAs("nepali.txt", Encoding.GetEncoding(1252))应该返回false,但它却返回true。

public static bool CanBeReadAs(string filePath, Encoding encoding)
    {
        // make it strict:
        encoding = Encoding.GetEncoding(encoding.CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);
        using (var r = new StreamReader(filePath, encoding, false))
        {
            try
            {
                r.ReadToEnd();
            }
            catch (Exception e)
            {
                //swallow
                return false;
            }
        }
        return true;
    }

我还尝试过用“string s = r.ReadToEnd();”来确保数据真的被强制解码,但这似乎没有影响。

我哪里做错了?

注意:如果我需要特别处理BOM(字节顺序标记),请告诉我。我倾向于忽略它们,如果这样做简单的话。(顺便说一下,这些文件有混合编码,不过我希望任何以BOM开头的文件都是纯unicode。)

这是我创建的一个Python脚本,它使用了相同的策略,并且运行良好:

def bad_encoding(filename, enc='utf-8', max=9):
'''Return a list of up to max error strings for lines in the file not encoded in the specified encoding. 

Otherwise, return an empty list.'''

errors = []
line = None
with open(filename, encoding=enc) as f:
    i = 0
    while True:
        try:
            i += 1
            line = f.readline()
        except UnicodeDecodeError:
            errors.append('UnicodeDecodeError: Could not read line {} as {}.'.format(i, enc))
        if not line or len(errors) > max:
            break

return errors

1 个回答

8

通过Encoding类可以找到一些静态的编码实例,比如Ascii、UTF8、Unicode等。这些编码实例在解码输入的字节时会尽量做到最好,即使解码失败也不会报错。

如果你想创建一个具有特定编码/解码行为的Encoding实例,可以使用Encoding.GetEncoding的重载版本,这个版本需要传入EncoderFallback和DecoderFallback参数。我尝试创建了各种编码的实例(比如AsciiEncoding和UTF8Encoding),但它们是只读的,所以设置回退选项时总是会抛出InvalidOperationException异常。如果你想创建一个在解码失败时会报错的实例,可以试试:

encoding = Encoding.GetEncoding(encoding.CodePage, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);

撰写回答