1

我有一个 C# 方法需要检索字符串的第一个字符,并查看它是否存在于包含特定 unicode 字符(所有从右到左字符)的 HashSet 中。

所以我在做

var c = str[0];

然后检查哈希集。

问题是此代码不适用于第一个字符的代码点大于 65535 的字符串。

我实际上创建了一个循环,遍历从 0 到 70,000 的所有数字(最高 RTL 代码点约为 68,000,所以我四舍五入),我从数字创建一个字节数组,并使用

Encoding.UTF32.GetString(intValue);

用这个字符创建一个字符串。然后我将它传递给在 HashSet 中搜索的方法,该方法失败,因为当它获取

str[0]

这个价值永远不是它应该的样子。

我究竟做错了什么?

4

3 回答 3

5

AString是 UTF-16 代码单元的序列,一个或两个编码一个 Unicode 代码点。如果要从字符串中获取代码点,则必须迭代字符串中的代码点。“字符”也是一个基本代码点,后跟零个或多个组合代码点(“组合字符”)的序列。

// Use a HashSet<String>

var itor = StringInfo.GetTextElementEnumerator(s);
while (itor.MoveNext()) {
    var character = itor.GetTextElement();
    // find character in your HashSet
}

如果您不需要考虑组合代码点,则可以将它们清除。(但它们在某些语言中非常重要。)

于 2016-10-18T16:39:52.537 回答
1

对于将来看到这个问题并且对我最终得到的解决方案感兴趣的任何人 - 这是我的方法,它根据字符串中的第一个字符来决定字符串应该显示 RTL 还是 LTR。它考虑了 UTF-16 代理对。

感谢 Tom Blodget,他为我指明了正确的方向。

if (string.IsNullOrEmpty(str)) return null;

var firstChar = str[0];
if (firstChar >= 0xd800 && firstChar <= 0xdfff)
{
    // if the first character is between 0xD800 - 0xDFFF, this is the beginning
    // of a UTF-16 surrogate pair. there MUST be one more char after this one,
    // in the range 0xDC00-0xDFFF. 
    // for the very unreasonable chance that this is a corrupt UTF-16 string
    // and there is no second character, validate the string length
    if (str.Length == 1) return FlowDirection.LeftToRight;

    // convert surrogate pair to a 32 bit number, and check the codepoint table
    var highSurrogate = firstChar - 0xd800;
    var lowSurrogate = str[1] - 0xdc00;
    var codepoint = (highSurrogate << 10) + (lowSurrogate) + 0x10000;

    return _codePoints.Contains(codepoint)
        ? FlowDirection.RightToLeft
        : FlowDirection.LeftToRight;
}
return _codePoints.Contains(firstChar)
    ? FlowDirection.RightToLeft
    : FlowDirection.LeftToRight;
于 2016-10-25T10:03:21.430 回答
0

我不确定我是否理解您的问题,一小段代码可能有用。当你有像'var c = str[0]'这样的行时,假设'str'是一个字符串,那么c将是一个字符,它是UTF16编码的。因为这个 c 永远不会大于 (2^16 - 1)。Unicode 字符可以比这更大,但是当这种情况发生时,它们被编码为跨越多个“字符”位置。在 UTF-16 的情况下,“第一个”字符可能占用 1 个或 2 个 16 位值。

于 2016-10-18T16:39:59.900 回答