【问题标题】:uint and char casting for unicode character code用于 unicode 字符代码的 uint 和 char 转换
【发布时间】:2015-05-19 11:30:22
【问题描述】:

谁能解释一下这段代码到底发生了什么:

var letter= 'J';
char c = (char)(0x000000ff & (uint)letter);

我知道它正在获取字符的 unicode 表示,但是我不完全理解以下角色的作用:

(0x000000ff & (uint)letter

0x000000ff 的目的和将字母转换为 (uint) 的目的是什么?是否有一种简便的方法可以达到相同的结果?

谢谢

更新

好吧,看起来大多数人都认为这是一个不好的例子,我不想包括整个班级,但我想我也可以这样你就可以看到上下文。来自Reference Source's WebHeaderCollection

  private static string CheckBadChars(string name, bool isHeaderValue)
    {
        if (name == null || name.Length == 0)
        {
            // emtpy name is invlaid
            if (!isHeaderValue)
            {
                throw name == null ? 
                    new ArgumentNullException("name") :
                    new ArgumentException(SR.GetString(SR.WebHeaderEmptyStringCall, "name"), "name");
            }

            // empty value is OK
            return string.Empty;
        }

        if (isHeaderValue)
        {
            // VALUE check
            // Trim spaces from both ends
            name = name.Trim(HttpTrimCharacters);

            // First, check for correctly formed multi-line value
            // Second, check for absenece of CTL characters
            int crlf = 0;
            for (int i = 0; i < name.Length; ++i)
            {
                char c = (char)(0x000000ff & (uint)name[i]);
                switch (crlf)
                {
                    case 0:
                        if (c == '\r')
                        {
                            crlf = 1;
                        }
                        else if (c == '\n')
                        {
                            // Technically this is bad HTTP.  But it would be a breaking change to throw here.
                            // Is there an exploit?
                            crlf = 2;
                        }
                        else if (c == 127 || (c < ' ' && c != '\t'))
                        {
                            throw new ArgumentException(SR.GetString(SR.WebHeaderInvalidControlChars), "value");
                        }

                        break;

                    case 1:
                        if (c == '\n')
                        {
                            crlf = 2;
                            break;
                        }

                        throw new ArgumentException(SR.GetString(SR.WebHeaderInvalidCRLFChars), "value");

                    case 2:
                        if (c == ' ' || c == '\t')
                        {
                            crlf = 0;
                            break;
                        }

                        throw new ArgumentException(SR.GetString(SR.WebHeaderInvalidCRLFChars), "value");
                }
            }

            if (crlf != 0)
            {
                throw new ArgumentException(SR.GetString(SR.WebHeaderInvalidCRLFChars), "value");
            }
        }
        else
        {
            // NAME check
            // First, check for absence of separators and spaces
            if (name.IndexOfAny(InvalidParamChars) != -1)
            {
                throw new ArgumentException(SR.GetString(SR.WebHeaderInvalidHeaderChars), "name");
            }

            // Second, check for non CTL ASCII-7 characters (32-126)
            if (ContainsNonAsciiChars(name))
            {
                throw new ArgumentException(SR.GetString(SR.WebHeaderInvalidNonAsciiChars), "name");
            }
        }

        return name;
    }

感兴趣的是:

char c = (char)(0x000000ff & (uint)name[i]);

【问题讨论】:

  • var letter= "J"; 应该是var letter= 'J'
  • @xanatos 感谢您的更正,这是一个错字。

标签: c# .net unicode


【解决方案1】:

您正在解析 HTTP 标头,对吗?这意味着您不应该使用(任何)unicode 编码。

HTTP 标头必须是 7 位 ASCII(与请求数据不同)1。这意味着您应该使用 ASCII 编码而不是默认编码。因此,当您解析请求字节时,您必须使用Encoding.ASCII.GetString 而不是Encoding.Default.GetString。希望您没有使用 StreamReader - 这将是一个坏主意,原因有很多,包括(可能)标头和请求内容之间的编码不匹配。

编辑:

至于在 Microsoft 源代码中的使用 - 是的,它发生了。不要试图复制这些类型的东西 - 它一个黑客。请记住,您没有 Microsoft 工程师所拥有的测试套件和质量保证,因此即使它确实有效,您最好不要复制此类黑客。

我假设它是这样处理的,因为 string 用于原则上应该是“ASCII 字符串”或只是 byte[] 的东西 - 因为 .NET 只支持 unicode 字符串,这被视为较小的邪恶(事实上,这就是为什么代码明确检查 string 包含任何 unicode 字符 - 它很清楚标头 必须 是 ASCII - 如果字符串包含任何非 ASCII 字符,它将显式失败。这只是为其他人编写高性能框架时通常的权衡。

脚注:

  1. 实际上,RFC (2616) 指定 US-ASCII 作为编码,大概意思是 ISO-8859-1。然而,RFC 不是一个绑定标准(它更像是希望摆脱混乱:D),并且周围有很多 HTTP/1.0 和 HTTP/1.1 客户端(和服务器)实际上并不尊重这一点。像 .NET 作者一样,我会坚持使用 7 位 ASCII(编码的每字节字符,当然,不是 真正的 7 位)。

【讨论】:

  • HTTP 标头可以是 ISO-8859-1 (tools.ietf.org/html/rfc5987 默认情况下,超文本传输​​协议 (HTTP) 消息中的消息标头字段参数不能携带 ISO-8859-1 字符之外的字符set) 但要点是一样的:1 byte 变为 1 char,映射 1:1 的值。
  • @xanatos 哈,我想知道它是否一直存在(即使是原始的 HTTP 1.1 RFC 2616 也谈到了“US-ASCII”,这可能意味着 ISO-8859-1)。显然,它还允许在 5987 中使用 UTF-8。在任何情况下,大量的客户端都假设 ASCII,所以坚持使用 7 位 ASCII 仍然是一个好主意(也就是说,每个 8 位都是零,而不是 真正的 7 位。那会很傻)。就像您不指定字符集时一样,它意味着 iso-8859-1,而大多数客户认为这意味着客户应该猜测编码:D
  • @Luaan 尽管如此,如果你使用ISO-8859-1,它的优点是在值为0-255的字节和值为0-255的char之间存在1:1的转换,所以它总是安全的。然后你可以用另一个编码器重新解码它们而不会丢失信息。
  • @Luaan 你是个很棒的治疗师! :-) 非常感谢您的简洁回答。
  • @xanatos 是的,这绝对是一个好点。这意味着在途中不会丢失任何数据。当然,它仅适用于 .NET 用于其字符串的变体 - 例如,使用 UTF-8 意味着您会通过剥离额外的字节而丢失(嗯,破坏)127 以上的字符。 Ä 在 ISO-8859-1 和 UTF-16 中很好(只是一个简单的 196 单字节,UTF-16 有前导零字节),而在 UTF-8 中,它是 195, 132。剥离第一个字节会留下 而不是Ä
【解决方案2】:

0x000000ff 的目的和将字母转换为 (uint) 的目的是什么

使用 [0..255] 范围内的代码获取字符:char 在内存中占用 2 个字节

例如:

var letter= (char)4200; // ၩ
char c = (char)(0x000000ff & (uint)letter); // h

// or
// char c = (char)(0x00ff & (ushort)letter);

// ushort (2-byte unsigned integer) is enough: uint is 4-byte unsigned integer

【讨论】:

    【解决方案3】:

    此代码所做的不是转换为 Unicode。如果有的话,情况正好相反:

    0x000000ff &amp; 部分基本上是丢弃了 unicode 字母的第二个字节,并将其转换为只有一个字节长的字母。或者更准确地说:它只保留最重要的字节并丢弃所有其他字节 - 这与 char 相同,因为它的大小为两个字节。

    我仍然认为这没有意义,因为它会导致误报:实际使用两个字节的 Unicode 字母只会丢失其中一个字节因此变成不同的字母 我会简单地摆脱此代码并在您使用c 的任何地方使用name[i]

    【讨论】:

    • 我不会说 second - unicode 每个字符的字节数可以多于两个,也可以更少。它只占用最低字节,无论其他字节是什么样的(或有多少)。
    • @Luaan:C#中的char类型是两个字节,所以不可能超过两个字节。
    • 是的,但这比char 级别更高——这意味着这些字符实际上将被表示为一个代理对——这甚至更更糟——不是只有你不会去掉一半的 unicode 字母,实际上你会添加没有任何意义的伪 ASCII 字符。我知道你并不是说使用这样的东西是个好主意,但它比仅仅改变输出中的一些字母还要复杂。 char 永远只有两个字节,但 unicode 字母可以有多个 chars。
    猜你喜欢
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    • 2011-06-18
    • 2013-04-29
    • 2018-09-18
    • 2012-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多