.net 按字母而不是字符枚举字符串

.net 按字母而不是字符枚举字符串,.net,string,unicode,.net,String,Unicode,字符串通常按字符枚举。但是,特别是在使用Unicode和非英语语言时,有时我需要按grapheme枚举字符串。也就是说,组合标记和变音符号应与它们修改的基本字符保持一致。在.Net中执行此操作的最佳方法是什么 用例:计算一系列单词中不同的语音 简化定义:图形和声音之间存在一对一的关系 现实定义:基础字符(例如pʰ)中还应包含特殊的“字母式”字符,有些声音可能由两个由系杆(k͡p)连接的符号表示 简化情景 这是非常有用和有效的: private static List<SoundCount&

字符串通常按字符枚举。但是,特别是在使用Unicode和非英语语言时,有时我需要按grapheme枚举字符串。也就是说,组合标记和变音符号应与它们修改的基本字符保持一致。在.Net中执行此操作的最佳方法是什么

用例:计算一系列单词中不同的语音

  • 简化定义:图形和声音之间存在一对一的关系
  • 现实定义:基础字符(例如pʰ)中还应包含特殊的“字母式”字符,有些声音可能由两个由系杆(k͡p)连接的符号表示
  • 简化情景 这是非常有用和有效的:

    private static List<SoundCount> CountSounds(IEnumerable<string> words)
    {
        Dictionary<string, SoundCount> soundCounts = new Dictionary<string, SoundCount>();
    
        foreach (var word in words)
        {
            TextElementEnumerator graphemeEnumerator = StringInfo.GetTextElementEnumerator(word);
            while (graphemeEnumerator.MoveNext())
            {
                string grapheme = graphemeEnumerator.GetTextElement();
    
                SoundCount count;
                if (!soundCounts.TryGetValue(grapheme, out count))
                {
                    count = new SoundCount() { Sound = grapheme };
                    soundCounts.Add(grapheme, count);
                }
                count.Count++;
            }
        }
    
        return new List<SoundCount>(soundCounts.Values);
    }
    

    我们可能还可以使用CharUnicodeInfo.getUnicodeCegory创建自己的IEnumerator来恢复性能,但这对我来说似乎太多的工作和额外的代码需要维护。(还有人想试试吗?)正则表达式就是为此而设计的。

    我不确定这正是您想要的,但您的问题是否与Unicode规范化有关

    当字符串规范化为Unicode格式C(这是默认格式)时,变音符号和它们修改的字符将组合在一起,因此,如果枚举字符,则将同时获得基本字符和修改字符

    将其规范化为表单D时,基字符和修饰符分开,并在枚举中分别返回


    请参阅方法了解详细信息。

    注意:我特意回答了我自己的问题以添加到Stackoverflow knowledgebase(),因为我认为这可能有助于其他人解释我发现的一个晦涩难懂(至少对我来说是晦涩难懂)的类,即
    TextElementEnumerator
    ,以及我偶然发现的一些限制。如果您有其他想法要补充,请这样做@托马斯:对讨论的补充很好。在我的情况下,规范化失败了,因为只有当组合实际上能够解析为单个已知的unicode字符时,规范化才起作用。我们使用的许多字形(特别是用于语音的字形)都不能:即使它们显示为一个字符,但该字符在unicode中并不存在。但在许多情况下,标准化将完美地处理它。谢谢这应该对其他人有帮助!
    private static List<SoundCount> CountSoundsRegex(IEnumerable<string> words)
    {
        var soundCounts = new Dictionary<string, SoundCount>();
        var graphemeExpression = new Regex(@"\P{M}\p{M}*");
    
        foreach (var word in words)
        {
            Match graphemeMatch = graphemeExpression.Match(word);
            while (graphemeMatch.Success)
            {
                string grapheme = graphemeMatch.Value;
    
                SoundCount count;
                if (!soundCounts.TryGetValue(grapheme, out count))
                {
                    count = new SoundCount() { Sound = grapheme };
                    soundCounts.Add(grapheme, count);
                }
                count.Count++;
    
                graphemeMatch = graphemeMatch.NextMatch();
            }
        }
    
        return new List<SoundCount>(soundCounts.Values);
    }
    
    [\P{M}\P{Lm}]      # Match a character that is NOT a character intended to be combined with another character or a special character that is used like a letter
    (?:                # Start a group for the combining characters:
      (?:                # Start a group for tied characters:
        [\u035C\u0361]      # Match an under- or over- tie bar...
        \P{M}\p{M}*         # ...followed by another grapheme (in the simplified sense)
      )                  # (End the tied characters group)
      |\p{M}             # OR a character intended to be combined with another character
      |\p{Lm}            # OR a special character that is used like a letter
    )*                 # Match the combining characters group zero or more times.