代码之家  ›  专栏  ›  技术社区  ›  Isaiah Shiner

带有自定义StringComparer的IndexOf

  •  3
  • Isaiah Shiner  · 技术社区  · 8 年前

    为什么 String.IndexOf(String, StringComparison) 需要一个 StringComparison 不允许更一般的 StringComparer ,甚至只是 IComparer<T> IEqualityComparer<T> ?

    我定制了一个 字符串比较器 我想在我的项目的其他部分使用它,但如果没有大量的扩展方法,我找不到一个很好的方法来做到这一点,如果这些方法甚至可以工作的话。

    这是我做的比较器。它大致基于以下建议: Implementing custom IComparer with string

    还要注意,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;
        }
    }
    
    2 回复  |  直到 8 年前
        1
  •  2
  •   NetMage    4 年前

    使用方便的扩展 IEnumerable 看起来应该已经有了,你可以写一个 String 扩展以使用 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 Equal 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 Equal to one of the substrings beginning at this position
                                 Enumerable.Range(1, source.Length-i).Any(ml => sc.Equals(source.Substring(i, ml), match)),
                                 -1 // else return -1 if no position matches
                              );
        }
    }
    

    注意:修改为在源子字符串和匹配字符串长度可能不相等时正确处理。

        2
  •  1
  •   Tom Bogle    4 年前

    以防任何人需要一个与 IComparer<String> ,这里是对@NetMage发布的好解决方案的一个微不足道的修改:

            public static int IndexOf(this string source, string match, IComparer<String> sc) {
            return Enumerable.Range(0, source.Length) // for each position in the string
                .FirstOrDefault(i => // find the first position where either
                        // match is Equal at this position for length of match (or to end of string) or
                        sc.Compare(source.Substring(i, Math.Min(match.Length, source.Length-i)), match) == 0 ||
                        // match is Equal to one of the substrings beginning at this position
                        Enumerable.Range(1, source.Length-i).Any(ml => sc.Compare(source.Substring(i, ml), match) == 0),
                    -1 // else return -1 if no position matches
                );
        }