如何在C#中获取包含表情符号的字符串的正确长度

7

英国国旗表情符号 包含 14 个字节的数据,当它们组合在一起时将呈现一个单一的字符 -

如果我的代码看起来像这样:

var test = "\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f";

Console.WriteLine(test);
Console.WriteLine(test.Length);

它将打印字符和数字14。我希望它能返回1。在搜索答案时,我找到了这个解决方案:
var stringInfo = new System.Globalization.StringInfo(test);
Console.WriteLine(stringInfo.LengthInTextElements);

问题在于,它输出的是7。我猜测它将其解释为双字节unicode并只给了我一半的字节长度。请参见这个dotnetfiddle以获取一个可工作的示例。
如何获得字符串表示为图形数的数量?
这里有一个类似的在Swift中编写的测试,在OSX上运行XCode,它显然正按照我的要求工作,但我需要它在C#中。

Swift playground


1
如果您将test初始化代码替换为var test = Char.ConvertFromUtf32(0x1F3F4).ToString();,则将显示预期的长度。也许您有一些额外的字符或其他东西在其中? - itsme86
0x1F3F4不是同一个表情符号,它只是一面普通的黑旗。我示例中的表情符号是英格兰国旗,https://emojipedia.org/flag-for-england/,需要所有字节才能呈现出完全相同的国旗。在电脑上,在浏览器中,它们将呈现相同的国旗,但在iOS或Android上,您将看到英格兰国旗。 - TheQ
1
据我所知,无法确定7个代码点是否显示为单个“字符”。我发现这篇文章非常有启发性。链接:https://manishearth.github.io/blog/2017/01/14/stop-ascribing-meaning-to-unicode-code-points/ - itsme86
4
这个问题并没有令人满意的答案。显示文本长度是操作系统文本渲染引擎的一个非常重要的实现细节。在Windows上,有许多这样的引擎,这是为了保持几十年前编写的程序能够在新的操作系统版本上运行的必然结果。而这是苹果从来不用担心的问题。你所得到的引擎很大程度上取决于你创建的具体项目类型。它们都有一种方式来测量显示文本的长度,但不总是产生相同的结果。在控制台模式应用程序中,你实际上得到了正确的结果,因为控制台根本无法显示表情符号 :) - Hans Passant
问题在于文本内容可能包含任何字符,而非仅仅是表情符号。我希望得到与 Swift 统计相同的结果。但 @hans-passant 说的也许是对的:这个问题可能没有好的答案 :/ - TheQ
显示剩余2条评论
4个回答

4

在 .NET 5 中,只需使用 StringInfo.LengthInTextElements。该方法在早期版本的 .NET 中对这些表情符号的处理存在错误。您可以查看此博客

在我的 C# 互动窗口(.NET Core 模式)中,我得到了这个结果:

> Environment.Version
[5.0.7]
> var stringInfo = new System.Globalization.StringInfo("");
. Console.WriteLine(stringInfo.LengthInTextElements);
1

欢迎来到 Stack Overflow!如果您在回答中展示一个实现的例子会更好。 - D J
@DJ 谢谢,我已经更新了答案并附上了一个例子。 - RcINS

3

您可以在此阅读文档:https://docs.google.com/document/d/1pC7N32TnmDr2xzFW4HscA1DyAPPZnwILUH2_03UL6Jo/preview

基于此,以下是似乎有效的内容:
安装NuGet包:

并尝试此代码:

class Program
{
    static void Main(string[] args)
    {
        Icu.Wrapper.Init();
        var test = new string[]
        {
            "\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f",
            "\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F",
            "e\u0301",
            "\U0001F468\U0001F3FF", 
        };

        foreach (var t in test)
        {
            var len = GetLen(t);
            Console.WriteLine(len);
        }
    }

    static int GetLen(string test)
    {
        var ci = Icu.BreakIterator.CreateCharacterInstance(new Icu.Locale("en_US"));
        ci.SetText(test);
        int len = 0;
        while (ci.MoveNext() != Icu.BreakIterator.DONE)
        {
            len++;
        }
        return len;
    }
}

Windows控制台无法显示这些表情符号,但您可以在Visual Studio的监视或立即窗口中检查它们。

希望对您有所帮助,Tom


哇,它完美地运行了!我刚刚尝试了一个包含大约2500个表情符号的数据库,它们都被计算为长度1。它还可以完美地计算包含表情符号的字符串的长度。谢谢! - TheQ

2

从您的链接中:

英格兰表情符号是挥舞着黑旗、标记拉丁小写字母G、标记拉丁小写字母B、标记拉丁小写字母E、标记拉丁小写字母N、标记拉丁小写字母G和取消标记表情符号的序列。

这些是7个Unicode字符,所以答案7是正确的。显示Unicode字符的规则很复杂,在某些系统上它会显示英格兰国旗,但在我的系统上不会。

您可以尝试使用ScriptItemize函数或GetGlyphIndicesW函数来为您计数。


1
虽然有7个Unicode字符,但这并不是“证明”7应该是输出结果。例如(new System.Globalization.StringInfo("e\u0301")).LengthInTextElements即使"e\u0301"是两个Unicode字符,也会返回1。但是,这两个字符组合成一个等同于é的字形。 - Jeppe Stig Nielsen

0

长评论(也许不是真正的答案)。

序列"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f"是一个相当新的Unicode标准,例如参见Wikipedia: Tags (Unicode block)。目前在.NET中还不支持它(也许永远不会支持)。

编写自己的方法来支持它。

顺便说一句,不要写成:

"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f"

在我看来,更清晰的写法是:

"\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F"

当然,这是同一个字符串。

如果您的源代码文件支持Unicode编码,您也可以使用以下方法:

""

当然。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接