C# 拉丁语、汉语、西里尔语等的子字符串UTF-8

C# 拉丁语、汉语、西里尔语等的子字符串UTF-8,c#,.net,utf-8,C#,.net,Utf 8,在Windows Phone上,我希望将任何给定字符串的子字符串长度等于100个ASCII字符 长度显然是无用的,因为中文字符串每个字符使用3个字节,丹麦字符串每个字符使用2或4个字节,而俄语字符串每个字符使用4个字节 唯一可用的编码是UTF-8和UTF-16。那我该怎么办 这个想法是: private static string UnicodeSubstring(string text, int length) { var bytes = Encoding.UTF8.GetBytes(

在Windows Phone上,我希望将任何给定字符串的子字符串长度等于100个ASCII字符

长度显然是无用的,因为中文字符串每个字符使用3个字节,丹麦字符串每个字符使用2或4个字节,而俄语字符串每个字符使用4个字节

唯一可用的编码是UTF-8和UTF-16。那我该怎么办

这个想法是:

private static string UnicodeSubstring(string text, int length)
{
    var bytes = Encoding.UTF8.GetBytes(text);

    return Encoding.UTF8.GetString(bytes, 0, Math.Min(bytes.Length, length));
}

但是长度需要与每个字符使用的字节数正确地分开,因此最后一个字符总是正确地呈现。

一个选项是简单地遍历字符串,计算每个字符的字节数

如果您知道不需要处理BMP以外的字符,这相当简单:

public string SubstringWithinUtf8Limit(string text, int byteLimit)
{
    int byteCount = 0;
    char[] buffer = new char[1];
    for (int i = 0; i < text.Length; i++)
    {
        buffer[0] = text[i];
        byteCount += Encoding.UTF8.GetByteCount(buffer);
        if (byteCount > byteLimit)
        {
            // Couldn't add this character. Return its index
            return text.Substring(0, i);
        }
    }
    return text;
}
带有inutf8limit的公共字符串子字符串(字符串文本,int byteLimit)
{
int字节数=0;
char[]buffer=新字符[1];
for(int i=0;i字节限制)
{
//无法添加此字符。请返回其索引
返回text.Substring(0,i);
}
}
返回文本;
}

如果必须处理代理项对,则会变得稍微复杂一些:(

一个选项是简单地添加“字符”(如果需要支持代理项对,则包括它们)查看结果字符串是否转换为所需的正确数字。

一个方法是检查最后一个字符是否为正确数字,并删除一个字符,直到正确呈现为止

private static string UnicodeSubstring(string text, int length)
{
    var bytes = Encoding.UTF8.GetBytes(text);
    var result = Encoding.UTF8.GetString(bytes, 0, Math.Min(bytes.Length, length));

    while ('\uFFFD' == result[result.Length - 1])
    {
        result = result.Substring(0, result.Length - 1);
    }

    return result;
}

虽然这是一个非常古老的问题,但我认为正确的方法是使用类的。它的主要优点是.NET文档保证
net461
及以上版本,
StringInfo
保证符合Unicode标准8.0.0版:

来电人士须知 在内部,StringInfo类调用的方法 CharUnicodeInfo类确定字符的方法 类别。从.NETFramework 4.6.2开始,字符 分类基于Unicode标准8.0.0版。对于 NET Framework 4通过.NET Framework 4.6.1,它基于 Unicode标准版本6.3.0.在.NET核心中,它基于 Unicode标准,版本8.0.0

现在,如果Microsoft文档中没有关于如何调用SubstringByTextElements的示例,那么实际上如何调用SubstringByTextElements呢

StringInfo
类中,有一个注释说明:

  • 通过调用
    ParseCombiningCharacters
    方法来检索包含每个文本元素起始索引的数组。然后,您可以通过将这些索引传递给
    SubstringByTextElements
    方法来检索单个文本元素
因此:

  • 调用ParseCombinigCharacters以获取每个文本元素的起始索引
  • 使用第一步提供的索引调用SubstringByTextElements

  • 你是说“相当于100字节”吗不是ASCII字符,对字符来说是无用的,因为取决于编码的可变字节数。UTF 8是一个可变长度的多字节编码。每个字符可以使用1到4字节。因此,它不能由固定的字节数分隔。如果考虑每个“完整字符”的替代对实际字节数。在UTF8中,可能比4…+1还要多。我会使用Encoding.UTF8.GetBytes(stringWithCurrentCharacterOrPair)而不是ifs…Encoding.UTF8.GetByteCount(…)也可以为每个字符调用。有趣的想法。@AlexeiLevenkov:我试图避免为每个字符创建新字符串,但使用GetByteCount我们可以重用单个数组…编辑。@JonSkeet只是好奇,知道为什么C#没有内置子字符串来处理这个问题吗?@JohnZabroski:而不是
    text[I]
    ?如果必须处理代理项对,则可能会出现这种情况。如果这不是您的意思,我认为您应该在回答中提供完整的代码示例。您已说明应使用该方法,但没有说明应如何使用该方法来解决实际问题。