按文化意识的首字母对字符串进行分组

时间:2011-03-31 21:30:14

标签: .net sorting globalization cultureinfo string-comparison

我正在尝试按照首字母对字符串的排序列表进行分组。让我们说这是清单:

azaroth 
älgkebab 
orgel 
ölkorv

根据sv-SE对列表进行排序时,这是排序顺序:

azaroth 
orgel 
älgkebab 
ölkorv

这意味着按首字母分组将是

A
  azaroth
O
  orgel
Ä
  älgkebab
Ö 
  ölkorv

这是有道理的,这也是您在使用sv-SE的国家/地区的电话簿中找到它的方式。

根据en-US对列表进行排序时,这是排序顺序:

älgkebab 
azaroth 
ölkorv
orgel 

现在是有趣的部分。这意味着按首字母分组将是

AÄ
  älgkebab
  azaroth
OÖ
  ölkorv
  orgel

因为出于所有实际目的,在分类过程中“a”和“ä”被视为同一个字母,因此“o”和“ö”也是如此,这意味着它们就是为了这个目的相同的初始值。这是AFAIK如何在使用en-US的国家/地区的电话簿中找到它。

我的问题是,当文化因文化而异时,我怎样才能以编程方式实现这种分组?或者换句话说,在排序时,您如何知道哪些字母被视为“相同”?特定文化中的清单?

例如,我没有找到一种方法可以为“a”vs“ä”返回StringComparer 0

我有一个似乎有效的解决方案,这样做:

if (
    cultureInfo.CompareInfo.GetSortKey("a").KeyData[1] ==
    cultureInfo.CompareInfo.GetSortKey("ä").KeyData[1]
) // same initial (this will return false for sv-SE and true for en-US)

问题是,我不知道它是否适用于任何文化,甚至是KeyData SortKey数组中的第二个数据实际上是什么。 page on MSDN相当含糊,可能是有目的的。所以我宁愿有一个更可靠的解决方案。

2 个答案:

答案 0 :(得分:1)

当您在a中比较äsv-SE时,结果为-1,这样如果两个单词相同,除了变音符号,它们的排序总是相同。但你仍然可以发现它们的排序方式是相同的:将一些字符附加到其中一个字符,另一个字符与另一个字符不同,并进行比较。然后切换添加的字符并再次进行比较。如果结果不同,则字符的排序方式相同。

示例:

sv-SE:
"a0" < "ä1"
"a1" < "ä0"
en-US:
"a0" < "ä1"
"a1" > "ä0"

因此,在sv-SE'a' < 'ä',但在en-US 'a' == 'ä'。下面是一个根据这些规则对字符串列表进行分组的类。但它对某些文化并不适用,因为它们的排序顺序更复杂。例如,在捷克语中,ch被视为单独的字母,在h之后排序。我不知道你会如何解决这个问题。

此外,代码使用01作为要追加的字符。如果某些文化中的这些字符不会影响排序,则无法使用。

class Grouper
{
    StringComparer m_comparer;

    public Grouper(StringComparer comparer)
    {
        m_comparer = comparer;
    }

    public List<Tuple<string, List<string>>> Group(IEnumerable<string> strings)
    {
        List<Tuple<string, List<string>>> result =
            new List<Tuple<string, List<string>>>();

        var sorted = strings.OrderBy(s => s, m_comparer);

        string previous = null;

        List<char> currentGroupName = null;
        List<string> currentGroup = null;

        foreach (var s in sorted)
        {
            char sInitial = ToUpper(s[0]);
            if (currentGroup == null || !AreEqual(s[0], previous[0]))
            {
                if (currentGroup != null)
                    result.Add(Tuple.Create(
                        SortGroupName(currentGroupName),
                        currentGroup));
                currentGroupName = new List<char> { sInitial };
                currentGroup = new List<string> { s };
            }
            else
            {
                if (!currentGroupName.Contains(sInitial))
                    currentGroupName.Add(sInitial);
                currentGroup.Add(s);
            }

            previous = s;
        }

        if (currentGroup != null)
            result.Add(Tuple.Create(SortGroupName(currentGroupName), currentGroup));

        return result;
    }

    string SortGroupName(List<char> chars)
    {
        return new string(chars.OrderBy(c => c.ToString(), m_comparer).ToArray());
    }

    bool AreEqual(char c1, char c2)
    {
        return Math.Sign(m_comparer.Compare(c1 + "0", c2 + "1")) ==
            -Math.Sign(m_comparer.Compare(c1 + "1", c2 + "0"));
    }

    char ToUpper(char c)
    {
        return c.ToString().ToUpper()[0];
    }
}

此外,此类远离生产质量,例如,它不处理null或空字符串。

答案 1 :(得分:0)

它可能是一个实现内部值,类似于常量。价值本身并不重要,只与它与其他相关价值进行比较。

这类似于(例如)C中的EOF值。虽然GCC将其定义为-1,但实际值可能会有所不同,因此最终开发人员代码应仅比较该值,而不是对其进行评估。