如何忽略大小写比较字符的正确方法?

64
我想知道忽略大小写比较两个字符的正确方法,适用于所有文化。而且,Comparer<char>.Default 是否是测试两个字符而不忽略大小写的最佳方法?这对代理对起作用吗?
编辑:添加了示例IComparer<char>实现
如果对任何人有帮助,这就是我决定使用的内容。
public class CaseInsensitiveCharComparer : IComparer<char> {
    private readonly System.Globalization.CultureInfo ci;
    public CaseInsensitiveCharComparer(System.Globalization.CultureInfo ci) {
        this.ci = ci;
    }
    public CaseInsensitiveCharComparer()
        : this(System.Globalization.CultureInfo.CurrentCulture) { }
    public int Compare(char x, char y) {
        return Char.ToUpper(x, ci) - Char.ToUpper(y, ci);
    }
}

// Prints 3
Console.WriteLine("This is a test".CountChars('t', new CaseInsensitiveCharComparer()));

ToUpper 可以将字符转换为当前区域设置下的正确大写形式,但返回的词法顺序不正确。可能这仅在 .NET 中支持字符串比较。 - Holstebroe
9个回答

103

这取决于您对“适用于所有文化”的理解是什么。您是否希望在土耳其,即使大小写不同,“i”和“I”也被视为相等呢?

您可以使用:

bool equal = char.ToUpperInvariant(x) == char.ToUpperInvariant(y);

...但我不确定在你理解的“有效”范围内,是否适用于所有文化。

当然,您可以将这两个字符都转换为字符串,然后对字符串执行任何比较。虽然效率稍低,但它确实提供了框架中可用的所有比较范围:

bool equal = x.ToString().Equals(y.ToString(), 
                                 StringComparison.InvariantCultureIgnoreCase);

对于代理对(surrogate pairs),Comparer<char> 并不可行,因为你没有单个的 char。但你可以创建一个 Comparer<int>


这就是我在你的两个示例中考虑过的方式,但我认为可能有一种更好的方式,即我不知道框架提供的方式。我是在考虑 String.Contains(char, IEqualityComparer<char>) 的 LINQ 扩展方法的上下文中思考的。 - Brett Ryan
3
没有适用于此的框架方法:字符串比较实际上是使用本机方法实现的,而不是通过转到 Comparer<char> 实现来完成。 - Julian Birch
@TimSchmelter:不,由于某种原因错过了那个。在结尾处添加了一个快速注释。 - Jon Skeet
这个解决方案并不是很高效。如果你有两个长度为十亿字符的字符串,其中第一个字符已经不同了,那么 x.ToString() 将枚举所有字符,这是相当浪费的,因为在第一个字符后就可以停止比较。 - Harald Coppoolse
@HaraldCoppoolse:除了在这个问题的上下文中,x是一个单一的字符。 - Jon Skeet

15

使用默认(即不变)区域设置:

if (char.ToLower(ch1) == char.ToLower(ch2))
{  ....  }

或者指定一个文化:
CultureInfo myCulture = ...;
if (char.ToLower(ch1, myCulture) == char.ToLower(ch2, myCulture))
{  ....  }

我不能给你点踩,但是我给了你一个赞,因为我认为你的解决方案非常恰当地回答了问题。 - Brett Ryan
这不是对问题的回答。 - Jon Grant

4
据我所知,没有一种方法适用于“所有文化”。如果您想比较字符以某种内部、不向用户显示的原因(在这种情况下,应使用InvariantCulture),或者您想使用用户的CurrentCulture。显然,使用用户的当前区域设置将意味着您将在不同的语言环境中获得不同的结果,但这些结果将与那些语言环境中的用户所期望的结果一致。
如果不知道您为什么要比较两个字符,我无法就应该使用哪个字符进行建议。

谢谢Jon,这是一个普遍的问题,我对Unicode不熟悉,所以想在这里提出问题。考虑到LINQ提供的String.Contains(char, IEqualityComparer<char>)扩展方法,如果要实现不区分大小写,应该采用什么正确的方式呢? - Brett Ryan
再次强调,这取决于数据的内容和比较的目的。例如,如果您只是想将事物按一致性顺序排序,使用任何一种不变的比较方法都可以。但如果您正在响应用户输入,则应该使用用户所在文化背景的比较方式,以给他们预期的结果。我不确定是否真的有“万能解决方案”。 - Jon Grant
你认为我作为答案提供的比较器实现是否是正确的方法? - Brett Ryan

1
我建议先比较大写字母,如果它们不匹配,则再比较小写字母,以防万一区域设置的大写和小写逻辑行为略有不同。
补充说明:
例如,
int CompareChar(char c1, char c2)
{
    int  dif;

    dif = char.ToUpper(c1) - char.ToUpper(c2);
    if (diff != 0)
        dif = char.ToLower(c1) - char.ToLower(c2);
    return dif;
}

0
我认为在运行时可用的内容应该类似于以下内容。
public class CaseInsensitiveCharComparer : IComparer<char> {
    private readonly System.Globalization.CultureInfo ci;
    public CaseInsensitiveCharComparer(System.Globalization.CultureInfo ci) {
        this.ci = ci;
    }
    public CaseInsensitiveCharComparer()
        : this(System.Globalization.CultureInfo.CurrentCulture) { }
    public int Compare(char x, char y) {
        return Char.ToUpper(x, ci) - Char.ToUpper(y, ci);
    }
}

// Prints 3
Console.WriteLine("This is a test".CountChars('t', new CaseInsensitiveCharComparer()));

1
假设在未来的CLR版本中,通过减法进行字符比较仍然是正确的,这是很危险的。因此,我建议使用return Char.ToUpper(x, ci).CompareTo(Char.ToUpper(y, ci)); - Matt Howells
@MattHowells 我认为...请参见char.CompareTo(char)return (m_value-value); - user57508

-1

你可以尝试:

    class Test{
    static int Compare(char t, char p){
        return string.Compare(t.ToString(), p.ToString(), StringComparison.CurrentCultureIgnoreCase);
    }
}

但我怀疑这不是“最优”的方法,但并非您需要检查的所有情况...


-2

string.Compare("string a","STRING A",true)

这将适用于每个字符串


1
嗨Sergio,我正在寻找一种比较两个char实例的方法,而不是字符串实例。我正在寻找一个忽略大小写的Comparer<char>实现。 - Brett Ryan
9
这在英语国家效果很好。然而,在东欧,没有人会使用你编写的应用程序。 - Jon Grant
2
@Jon Grant:我在我的国家(葡萄牙)使用它,葡萄牙语是一种以拉丁为基础的语言,有很多“奇怪”的字符,比如:ã é à ç,这对我来说完美地运作。 - Sergio

-3

我知道这是一个旧帖子,但是自那时以来情况已经发生了变化。

上面的问题可以通过使用扩展来回答。这将扩展char.Equals,以允许本地化和大小写不敏感性。

在扩展类中,添加如下内容:

internal static Boolean Equals(this Char src, Char ch, StringComparison comp)
{
    Return $"{src}".Equals($"{ch}", comp);
}

我现在正在工作,所以无法检查这个,但它应该可以工作。

安迪


2
非常糟糕的解决方案。涉及许多内存分配。 - Mabakay

-4

您可以将最后一个参数设置为 true,以进行不区分大小写的匹配

string.Compare(lowerCase, upperCase, true);

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