C#字符串比较方法返回第一个不匹配项的索引

21
有没有一种现有的字符串比较方法,根据两个字符串中第一次出现不匹配字符的位置返回一个值?
例如:
string A = "1234567890"

string B = "1234567880"

我希望能够获得一个值,以便我可以看到匹配断点的第一次出现是在A[8]。


2
自己编写是否不可能作为(也许是)扩展方法? - glosrob
8
+1 先检查标准库是否已存在此内容,然后翻译文本。 - Daren Thomas
完全正确。没有什么比完成一个非常好的实现后意识到它从1.1版本开始就在框架中存在更糟糕的事情了! - Jon Hanna
7个回答

7
/// <summary>
/// Gets a first different char occurence index
/// </summary>
/// <param name="a">First string</param>
/// <param name="b">Second string</param>
/// <param name="handleLengthDifference">
/// If true will return index of first occurence even strings are of different length
/// and same-length parts are equals otherwise -1
/// </param>
/// <returns>
/// Returns first difference index or -1 if no difference is found
/// </returns>
public int GetFirstBreakIndex(string a, string b, bool handleLengthDifference)
{
    int equalsReturnCode = -1;
    if (String.IsNullOrEmpty(a) || String.IsNullOrEmpty(b))
    {
        return handleLengthDifference ? 0 : equalsReturnCode;
    }

    string longest = b.Length > a.Length ? b : a;
    string shorten = b.Length > a.Length ? a : b;    
    for (int i = 0; i < shorten.Length; i++)
    {
        if (shorten[i] != longest[i])
        {
            return i;
        }
    }

    // Handles cases when length is different (a="1234", b="123")
    // index=3 would be returned for this case
    // If you do not need such behaviour - just remove this
    if (handleLengthDifference && a.Length != b.Length)
    {
        return shorten.Length;
    }

    return equalsReturnCode;
}

1
你为什么要检查 a.Equals(b) 而不是 a == b?如果 a 是 null,你的代码将会出现错误。 - Kevin Gosse
调用equals将迭代整个字符串,直到第一个断点除外的快捷情况,然后再迭代整个字符串。我会称之为“ReferenceEquals”作为一种快捷方式,但是省略掉“Equals”的其余部分,因为它的工作无论如何都会被重复。 - Jon Hanna
现在 "abc" 等于 null 吗?或者按照建议的替代方案,null 在其第一个字符上不匹配 null? - Jon Hanna
@Jon:现在null被视为零长度字符串,因此当启用“handleLengthDifference”时,可以将其处理为0索引处的差异。 - sll
如果a等于b,则返回-1并且必须返回a或b的长度,不是吗? - Marc

5

如果您已安装.NET 4.0,那么可以这样做:

    string A = "1234567890";
    string B = "1234567880";

    char? firstocurrence = A.Zip(B, (p, q) => new { A = p, B = q })
        .Where(p => p.A != p.B)
        .Select(p => p.A)
        .FirstOrDefault();

编辑:

如果您需要职位:

    int? firstocurrence = A.Zip(B, (p, q) => new { A = p, B = q })
            .Select((p, i) => new { A = p.A, B = p.B, idx = i })
            .Where(p => p.A != p.B)
            .Select(p => p.idx)
            .FirstOrDefault();

这不是问题中的要求。无论如何,如果两个字符串长度不同,很容易进行检查。 - Francisco
1
不完全是这样,因为要找出“abc123432343234”与“abcdefghijk”不匹配的地方,需要执行一些本来就可以回答问题的操作。 - Jon Hanna
1
LINQ是不正确的,由于Where子句它总是返回位置0。正确的代码应该在Zip之后加上:.Select((x, idx) => (item: x, idx)).Where(x => x.item.expected != x.item.actual).Select(x => x.idx).FirstOrDefault(); - dsschneidermann

2

以下是一个类似的扩展方法,可以完成此任务:

public static int Your_Name_Here(this string s, string other) 
{
    string first = s.Length < other.Length ? s : other;
    string second = s.Length > other.Length ? s : other;

    for (int counter = 0; counter < first.Length; counter++)
    {
        if (first[counter] != second[counter])
        {
            return counter;
        }
    }
    return -1;
}

如果 others 更短,会发生什么? - Oded
它出现了错误 :) 公平的评论,我会进行修改。 - glosrob
名称很糟糕 - 更喜欢@sll下面的答案 - glosrob
1
如果较长的字符串以较短的字符串开头,则返回 false -1。例如,"abc" 对于字符 3("abc" 根本没有该字符)与 "abcdef" 不匹配,但此函数将返回 -1 表示匹配。 - Jon Hanna
这个答案应该在多年前就被删除了。如果字符串不同但长度相同,则firstsecond都将被赋值为s,并且返回值将为-1。 - Mark Feldman

2
没有我所知道的,但这很简单:
public static int FirstUnmatchedIndex(this string x, string y)
{
  if(x == null || y == null)
    throw new ArgumentNullException();
  int count = x.Length;
  if(count > y.Length)
    return FirstUnmatchedIndex(y, x);
  if(ReferenceEquals(x, y))
    return -1;
  for(idx = 0; idx != count; ++idx)
    if(x[idx] != y[idx])
      return idx;
  return count == y.Length? -1 : count;
}

这是一个简单的序数比较。忽略大小写的序数比较很容易实现,但基于文化背景的比较定义却很棘手;例如,“Weißbier”与“WEISSBIERS”的最后一个S不匹配,但是它应该被算作第8个位置还是第9个位置呢?


1
嘿...我们都这样做...SO 应该整合一个在线编译器 ;) - Oded
@Oded 我自作自受,因为我称它为“琐碎”。虽然确实如此,但每个答案的初稿仍然存在缺陷。 - Jon Hanna

1

0

我的看法。 这里只是一个循环,比较两个字符串,一个叫做ExpectedHexStringParameterValue,另一个叫做defaultHexStringParVal。循环在第一次不匹配时中断。

int mismatchIndex = 0;
for (int i = 0; i <= ExpectedHexStringParameterValue.Length - 1; i++)
{
    Trace.WriteLine($"Go: {ExpectedHexStringParameterValue[i].ToString()}");
    if (i > defaultHexStringParVal.Length - 1)
      {
           mismatchIndex = i;
           break;
       }
    if (ExpectedHexStringParameterValue[i].ToString() != defaultHexStringParVal[i].ToString())
      {
           mismatchIndex = i;
           break;
      }
}

0

可以编写像字符串扩展这样的扩展。

public static class MyExtensions
{
    public static IList<char> Mismatch(this string str1, string str2)
    {
        var char1 = str1.ToCharArray();
        var char2 = str2.ToCharArray();
        IList<Char> Resultchar= new List<char>();
        for (int i = 0; i < char2.Length;i++ )
        {
            if (i >= char1.Length || char1[i] != char2[i])
                Resultchar.Add(char2[i]);
        }
        return Resultchar;
    }
}

使用方法如下:

var r = "1234567890".Mismatch("1234567880");

这不是一个寻找不匹配的优化算法。

如果你只想找到第一个不匹配的地方,

public static Char FirstMismatch(this string str1, string str2)
        {
            var char1 = str1.ToCharArray();
            var char2 = str2.ToCharArray();             
            for (int i = 0; i < char2.Length;i++ )
            {
                if (i >= char1.Length || char1[i] != char2[i])
                    return char2[i];
            }
            return ''c;
        }

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