【问题标题】:How to double-decode UTF-8 bytes C#如何双重解码 UTF-8 字节 C#
【发布时间】:2013-07-27 08:47:58
【问题描述】:

我有问题。 Unicode 2019 是这个字符: ’

这是一个正确的单引号。 它被编码为 UTF8。 但我担心它会被双重编码。

>>> u'\u2019'.encode('utf-8')
'\xe2\x80\x99'
>>> u'\xe2\x80\x99'.encode('utf-8')
'\xc3\xa2\xc2\x80\xc2\x99'
>>> u'\xc3\xa2\xc2\x80\xc2\x99'.encode('utf-8')
'\xc3\x83\xc2\xa2\xc3\x82\xc2\x80\xc3\x82\xc2\x99'
>>> print(u'\u2019')
’
>>> print('\xe2\x80\x99')
’
>>> print('\xc3\xa2\xc2\x80\xc2\x99')
’
>>> '\xc3\xa2\xc2\x80\xc2\x99'.decode('utf-8')
u'\xe2\x80\x99'
>>> '\xe2\x80\x99'.decode('utf-8')
u'\u2019'

This is the principle used above.

如何在 C# 中处理粗体部分?

如何获取 UTF8 编码的字符串,转换为字节数组,将其转换为字符串,然后再次解码?

我试过this的方法,但是输出的结果不适合ISO-8859-1,看来……

    string firstLevel = "’";
    byte[] decodedBytes = Encoding.UTF8.GetBytes(firstLevel);

    Console.WriteLine(Encoding.UTF8.GetChars(decodedBytes));
    // ’

    Console.WriteLine(decodeUTF8String(firstLevel));
    //â�,��"�
    //I was hoping for this:
    //’

了解更新:

Jon 帮助我解决了最基本的问题:从“’”到“’”,然后再到“'”但我想尊重他回答的核心建议:

  1. 了解正在发生的事情
  2. 修复原罪

我努力排在第一位。

编码/解码

我对这些术语感到非常困惑。 我将它们与加密/解密之类的术语混淆了,仅仅是因为“En...”和“De...” 我忘记了他们翻译的内容,以及他们翻译的内容。 我混淆了这些起点和终点;它是否与十六进制、字符实体、代码点和字符映射等其他模糊术语有关。

我想在基本层面上解决定义。 这个问题的上下文中的编码和解码是:

  1. 解码
    • 对应于 C# {Encoding}。'''GetString'''(bytesArray)
    • 对应Python stringObject.'''decode'''({Encoding})
    • 根据称为“编码”的某种转换方案,将字节作为输入,并转换为字符串表示形式作为输出,由上面的 {Encoding} 表示。
    • 字节 -> 字符串
  2. 编码
    • 对应于 C# {Encoding}。'''GetBytes'''(stringObject)
    • 对应 Python stringObject.'''encode'''({Encoding})
    • 解码的反面。
    • 字符串 -> 字节(Python 除外)

Python 中的字节与字符串

所以 Encode 和 Decode 带我们在字节和字符串之间来回切换。

虽然 Python 帮助我了解出了什么问题,但它也可能会混淆我对编码/解码“基础知识”的理解。 乔恩说:

可惜Python在很大程度上隐藏了【二进制数据和文本数据的区别】

我认为这就是 PEP 在it says: 时的意思

Python 的当前字符串对象被重载。它们用于保存字符序列和字节序列。这种重载目的会导致混乱和错误。

Python 3.* 不会以这种方式重载字符串。:

Python 2.7

>>> #Encoding example. As a generalization, "Encoding" produce bytes.
>>> #In Python 2.7, strings are overloaded to serve as bytes
>>> type(u'\u2019'.encode('utf-8'))
<type 'str'>

Python 3.*

>>> #In Python 3.*, bytes and strings are distinct
>>> type('\u2019'.encode('utf-8'))
<class 'bytes'>

Python 2 和 3 之间的另一个重要(相关)区别是它们的默认编码:

>>>import sys
>>>sys.getdefaultencoding()

Python 2

'ascii'

Python 3

'utf-8'

虽然 Python 2 说“ascii”,但我认为它的意思是 specific type of ASCII

  • “不是”是指 ISO-8859-1,它支持范围 (256),这是 Jon 用来解码的(下文讨论)
  • 表示ASCII,最简单的变种,只有range(128)

虽然 Python 3 不再将字符串作为字节和字符串重载,但解释器仍然可以很容易地忽略正在发生的事情并在类型之间移动。即

  • 只需在 Python 2.* 中的字符串前加一个 'u',它就是一个 Unicode 文字
  • 只需在 Python 3.* 中的字符串前加一个 'b',它就是一个字节字面量

编码和C

Jon 指出 C# 使用 UTF-16 来更正我上面的“UTF-8 编码字符串”注释;

每个字符串实际上都是 UTF-16。 我的理解是:如果C#有一个字符串对象“s”,那么计算机内存中其实有UTF-16 map.中那个字符对应的字节,也就是(包括byte-order-mark??)feff0073。

他还在我要求的破解方法中使用了 ISO-8859-1。 我不确定为什么。 我现在头很痛,等我有看法再回来。

我会回到这篇文章。我希望我解释得当。我会把它做成一个 Wiki 吗?

【问题讨论】:

  • .NET 中不存在“UTF-8 编码字符串”之类的东西。 每个 字符串实际上是 UTF-16。如果您可能可以,您应该修复表现不佳的代码(将二进制数据编码为文本)。
  • 谢谢。我需要对编码有更深入的了解。我将重读 Spolsky 等人的著作。是否可以在 C# 中从“’”转为“’”?不幸的是,我无法修复原罪。

标签: c# python encoding


【解决方案1】:

您需要了解从根本上这是由于有人误解了二进制数据和文本数据之间的区别。遗憾的是,Python 在很大程度上隐藏了这种差异——在 C# 中意外地执行这种特殊形式的双重编码是相当困难的。不过,这段代码应该适合你:

using System;
using System.Text;

class Test
{
    static void Main()
    {
        // Avoid encoding issues in the source file itself...
        string firstLevel = "\u00c3\u00a2\u00c2\u0080\u00c2\u0099";
        string secondLevel = HackDecode(firstLevel);
        string thirdLevel = HackDecode(secondLevel);
        Console.WriteLine("{0:x}", (int) thirdLevel[0]); // 2019
    }

    // Converts a string to a byte array using ISO-8859-1, then *decodes*
    // it using UTF-8. Any use of this method indicates broken data to start
    // with. Ideally, the source of the error should be fixed.
    static string HackDecode(string input)
    {
        byte[] bytes = Encoding.GetEncoding(28591)
                               .GetBytes(input);
        return Encoding.UTF8.GetString(bytes);
    }
}

【讨论】:

  • 谢谢你,乔恩。我既试图理解基本原理,又能找到原罪。我对您的回答没有特别理解的一部分:为什么 ISO-8859-1 相关?为什么它用于从字符串中获取字节?鉴于您的观点,我认为“UTF-16”可能是相关的:“每个字符串实际上都是 UTF-16。”并且是与这篇文章相关的答案:(我意识到它的 OP 做了类似的事情stackoverflow.com/q/1922199/1175496
  • @TheRedPea:ISO-8859-1 是一种将前 256 个 Unicode 字符转换为相同值的单个字节的编码 - 这就是这里的基本情况。 “双重编码”是将字符串编码为UTF-8,然后将结果(字节)视为字符串,但在ISO-8859-1中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-22
  • 1970-01-01
  • 2019-02-24
  • 2020-07-17
  • 2019-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多