C# 带有自定义StringComparer的IndexOf

C# 带有自定义StringComparer的IndexOf,c#,string,comparison,C#,String,Comparison,为什么String.IndexOf(String, StringComparison)需要StringComparison,不允许使用更通用的StringComparier,甚至不允许只使用iComparier或iQualityComparier 我制作了一个自定义的StringComparer来与几个字典一起使用,我想在我的项目的其他部分中使用它,但是如果没有很多扩展方法,我找不到一个很好的方法来实现这一点,如果这些扩展方法能够工作的话 这是我做的比较器。它大致基于这一建议: 还要注意,Mod

为什么
String.IndexOf(String, StringComparison)
需要
StringComparison
,不允许使用更通用的
StringComparier
,甚至不允许只使用
iComparier
iQualityComparier

我制作了一个自定义的
StringComparer
来与几个字典一起使用,我想在我的项目的其他部分中使用它,但是如果没有很多扩展方法,我找不到一个很好的方法来实现这一点,如果这些扩展方法能够工作的话

这是我做的比较器。它大致基于这一建议:

还要注意,ModifyString是一个WIP。我希望根据我比较的输入,在那里添加更多内容。我也知道它很贵,但我只是在寻找ATM的解决方案,而不是性能

public class CustomComparer : StringComparer
{
    public override int Compare(string x, string y)
    {
        return StringComparer.Ordinal.Compare(ModifyString(x), ModifyString(y));
    }

    public override bool Equals(string x, string y)
    {
        if (ModifyString(x).Equals(ModifyString(y)))
            return true;
        else
            return false;
    }

    public override int GetHashCode(string obj)
    {
        if (obj == null)
            return 0;
        else
            return ModifyString(obj).GetHashCode();
    }

    private string ModifyString(string s)
    {
        //I know this code is expensive/naaive, your suggestions are welcome.
        s = s.ToLowerInvariant();
        s = s.Trim();
        s = Regex.Replace(s, @"\s+", " ");//replaces all whitespace characters with a single space.
        return s;
    }
}

IEnumerable
使用一个方便的扩展似乎应该已经有了,您可以编写
String
扩展来使用
StringComparer
。正如注释中所建议的,所有可能的子字符串长度都在每个位置进行测试,因为无法对自定义
StringComparer
进行任何假设

public static class IEnumerableExt {
    public static T FirstOrDefault<T>(this IEnumerable<T> src, Func<T, bool> testFn, T defval) => src.Where(aT => testFn(aT)).DefaultIfEmpty(defval).First();
}

public static class StringExt {
    public static int IndexOf(this string source, string match, StringComparer sc) {
        return Enumerable.Range(0, source.Length) // for each position in the string
                         .FirstOrDefault(i => // find the first position where either
                             // match is Equals at this position for length of match (or to end of string) or
                             sc.Equals(source.Substring(i, Math.Min(match.Length, source.Length-i)), match) ||
                             // match is Equals to on of the substrings beginning at this position
                             Enumerable.Range(1, source.Length-i-1).Any(ml => sc.Equals(source.Substring(i, ml), match)),
                             -1 // else return -1 if no position matches
                          );
    }
}
公共静态类IEnumerableExt{
公共静态T FirstOrDefault(此IEnumerable src,Func testFn,T deffal)=>src.Where(aT=>testFn(aT)).DefaultIfEmpty(deffal).First();
}
公共静态类StringExt{
公共静态int IndexOf(此字符串源、字符串匹配、StringComparer sc){
返回字符串中每个位置的可枚举.Range(0,source.Length)//值
.FirstOrDefault(i=>//找到第一个位置,其中
//匹配在此位置等于匹配长度(或到字符串末尾)或
sc.Equals(source.Substring(i,Math.Min(match.Length,source.Length-i)),match)||
//匹配等于从该位置开始的子字符串的on
可枚举的.Range(1,source.Length-i-1).Any(ml=>sc.Equals(source.Substring(i,ml),match)),
-1//else如果没有位置匹配,则返回-1
);
}
}

示例总是很有用的。如何使用完全自定义的比较器进行搜索?您对子字符串的长度或任何内容一无所知。例如,要在
“horse”
中搜索
“x”
,您会将
“x”
与所有长度的子字进行比较吗?我必须假设,使用标准实现的问题是,它可能会假定您的StringComparer可能不具备的条件。使用-1提前退出的优化(例如一个输入为null,另一个不为null)在使用
NullMatches WhenTheStartStringBeginsWithTwoZeroessTringComparer
实现时可能无效。如果您有一个
StringComparer
,它可以减少重复的字符序列,例如为了查找索引匹配而将“aaabb”改为“ab”,那么它会变得更加混乱。我不确定您将如何确认您正在比较正确的子字符串。
“horse”.IndexOf(“x”,…)
。C#源代码是用URL编码的。@DanWilson添加了示例代码。效果非常好。我并不完全明白它是如何工作的,LINQ不是我的强项,但我会在评论中赞扬你,也许会在某个时候弄明白。谢谢大家!@我在代码中添加了一些注释以试图提供帮助。