【问题标题】:Get Difference Between Two Strings in Terms of Remove and Insert Actions在删除和插入操作方面获取两个字符串之间的差异
【发布时间】:2018-07-02 20:50:07
【问题描述】:

所以我有一个文本框,在文本更改事件中,我有旧文本和新文本,并希望了解它们之间的区别。在这种情况下,我希望能够使用一个删除功能和一个插入功能重新创建带有旧文本的新文本。这是可能的,因为文本框中的更改有几种可能性:

  1. 仅删除了文本(一个或多个使用选择的字符)- ABCD -> AD
  2. 仅添加了文本(一个或多个使用粘贴的字符)- ABCD -> ABXXCD
  3. 删除和添加文本(通过在同一操作中选择文本和输入文本)- ABCD -> AXD

所以我想拥有这些功能:

Sequence GetRemovedCharacters(string oldText, string newText)
{

}
Sequence GetAddedCharacters(string oldText, string newText)
{

}

我的序列类:

public class Sequence
{

    private int start;
    private int end;

    public Sequence(int start, int end)
    {
        StartIndex = start; EndIndex = end;
    }

    public int StartIndex { get { return start; } set { start = value; Length = end - start + 1; } }
    public int EndIndex { get { return end; } set { end = value; Length = end - start + 1; } }
    public int Length { get; private set; }

    public override string ToString()
    {
        return "(" + StartIndex + ", " + EndIndex + ")";
    }

    public static bool operator ==(Sequence a, Sequence b)
    {
        if(IsNull(a) && IsNull(b))
            return true;
        else if(IsNull(a) || IsNull(b))
            return false;
        else
            return a.StartIndex == b.StartIndex && a.EndIndex == b.EndIndex;
    }
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public static bool operator !=(Sequence a, Sequence b)
    {
        if(IsNull(a) && IsNull(b))
            return false;
        else if(IsNull(a) || IsNull(b))
            return true;
        else
            return a.StartIndex != b.StartIndex && a.EndIndex != b.EndIndex;
    }
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }

    static bool IsNull(Sequence sequence)
    {
        try
        {
            return sequence.Equals(null);
        }
        catch(NullReferenceException)
        {
            return true;
        }
    }

}

额外说明:我想知道哪些字符被删除以及哪些字符被添加到文本中以获得新文本,以便我可以重新创建它。假设我有 ABCD -> AXD。 “B”和“C”是被删除的字符,“X”是被添加的字符。因此,GetRemovedCharacters 函数的输出为 (1, 2),GetAddedCharacters 函数的输出为 (1, 1)。 GetRemovedCharacters 函数的输出指的是旧文本中的索引,GetAddedCharacters 函数的输出指的是删除删除字符后的旧文本中的索引。

编辑:我想到了几个方向:

  1. 我创建的这段代码*返回受影响的序列 - 如果字符被删除,它返回旧文本中删除的字符序列;如果添加了字符,则返回新文本中添加的字符序列。删除和添加文本时,它不会返回正确的值(我自己不确定我想要它是什么)。
  2. 也许文本框中的 SelectionStart 属性会有所帮助 - 更改文本后插入符号的位置。

*

private static Sequence GetChangeSequence(string oldText, string newText)
{
    if(newText.Length > oldText.Length)
    {
        for(int i = 0; i < newText.Length; i++)
            if(i == oldText.Length || newText[i] != oldText[i])
                return new Sequence(i, i + (newText.Length - oldText.Length) - 1);
        return null;
    }
    else if(newText.Length < oldText.Length)
    {
        for(int i = 0; i < oldText.Length; i++)
            if(i == newText.Length || oldText[i] != newText[i])
                return new Sequence(i, i + (oldText.Length - newText.Length) - 1);
        return null;
    }
    else
        return null;
}

谢谢。

【问题讨论】:

  • IsNull 函数的用途是什么?如果您试图避免==nulloperator== 中引起的堆栈溢出,请使用ReferenceEqual(a, null) [或c# 7 中的a is null]。不要依赖于抛出这样的 NRE。此外,通常最好根据== 定义!=(并根据Equals 定义==,尽管如果您实际上覆盖Equals,请确保您是GetHashCode 反映了这一点)
  • 恐怕你严重低估了这个问题。查找“差异”以获取更多信息。也许您可以使用 KeyPress 等事件并监视剪贴板来减少整个问题。仅 Textchanged 将使您面临整个未稀释的差异挑战。有关问题的示例,请参阅here
  • 你的任务是学习一些东西吗?重新实现什么?或者找出差异?我问是因为我有一个专门用于此任务的 nuget 包,difflibgithub 上的源代码。要获得差异,您只需执行Diff.CalculateSections(s1.ToCharArray(), s2.ToCharArray()) 并检查结果,并准确计算出发生了什么。现在,可以就这 3 种特定类型的更改回答您的问题,但是帮助您实现完全差异对于 Stack Overflow 来说过于宽泛。
  • 这三种变化的组合是唯一可能发生的事情吗?您没有添加 XX 并删除 DABCDE --&gt; AXXBDE,但不在字符串中的同一位置?
  • 简单修剪两个字符串两端的所有共同点,剩下的是改变的位。

标签: c# string text


【解决方案1】:

一个简单的字符串比较不会完成这项工作,因为您要求一种同时支持添加和删除字符的算法,因此不容易在几行代码中实现。我建议使用库而不是编写自己的比较算法。

this 项目为例。

【讨论】:

  • 那个项目基于太多的文本文件,我不知道我需要使用的实际算法在哪里。很高兴你能指出我在哪里,或者可能不是适合我
【解决方案2】:

我很快将这些信息汇总在一起,让您了解我为解决您的问题所做的工作。它不使用你的类,但它确实找到了一个索引,所以它可以为你定制。 这也有明显的局限性,因为它只是简单的框架。

此方法将通过将原始字符串与更改后的字符串进行比较来发现对原始字符串所做的更改

// Find the changes made to a string
string StringDiff (string originalString, string changedString)
{
    string diffString = "";

    // Iterate over the original string
    for (int i = 0; i < originalString.Length; i++)
    {
        // Get the character to search with
        char diffChar = originalString[i];

        // If found char in the changed string
        if (FindInString(diffChar, changedString, out int index))
        {
            // Remove from the changed string at the index as we don't want to match to this char again
            changedString = changedString.Remove(index, 1);
        }
        // If not found then this is a difference
        else
        {
            // Add to diff string
            diffString += diffChar;
        }
    }

    return diffString;
}

此方法将在第一次匹配时返回 true(这是一个明显的限制,但这更多是为了给您一个想法)

// Find char at first occurence in string
bool FindInString (char c, string search, out int index)
{
    index = -1;

    // Iterate over search string
    for (int i = 0; i < search.Length; i++)
    {
        // If found then return true with index
        if (c == search[i])
        {
            index = i;
            return true;
        }
    }

    return false;
}

这是一个简单的帮助方法,向您展示示例

void SplitStrings(string oldStr, string newStr)
{
    Console.WriteLine($"Old : {oldStr}, New: {newStr}");
    Console.WriteLine("Removed - " + StringDiff(oldStr, newStr));
    Console.WriteLine("Added - " + StringDiff(newStr, oldStr));
}

【讨论】:

  • 谢谢,但我的意思是删除和添加不是两个字符串之间的字符差异。
【解决方案3】:

我已经做到了。

static void Main(string[] args)
{

    while(true)
    {
        Console.WriteLine("Enter the Old Text");
        string oldText = Console.ReadLine();
        Console.WriteLine("Enter the New Text");
        string newText = Console.ReadLine();
        Console.WriteLine("Enter the Caret Position");
        int caretPos = int.Parse(Console.ReadLine());
        Sequence removed = GetRemovedCharacters(oldText, newText, caretPos);
        if(removed != null)
            oldText = oldText.Remove(removed.StartIndex, removed.Length);
        Sequence added = GetAddedCharacters(oldText, newText, caretPos);
        if(added != null)
            oldText = oldText.Insert(added.StartIndex, newText.Substring(added.StartIndex, added.Length));
        Console.WriteLine("Worked: " + (oldText == newText).ToString());
        Console.ReadKey();
        Console.Clear();
    }

}

static Sequence GetRemovedCharacters(string oldText, string newText, int caretPosition)
{
    int startIndex = GetStartIndex(oldText, newText);
    if(startIndex != -1)
    {
        Sequence sequence = new Sequence(startIndex, caretPosition + (oldText.Length - newText.Length) - 1);
        if(SequenceValid(sequence))
            return sequence;
    }
    return null;
}
static Sequence GetAddedCharacters(string oldText, string newText, int caretPosition)
{
    int startIndex = GetStartIndex(oldText, newText);
    if(startIndex != -1)
    {
        Sequence sequence = new Sequence(GetStartIndex(oldText, newText), caretPosition - 1);
        if(SequenceValid(sequence))
            return sequence;
    }
    return null;
}
static int GetStartIndex(string oldText, string newText)
{
    for(int i = 0; i < Math.Max(oldText.Length, newText.Length); i++)
        if(i >= oldText.Length || i >= newText.Length || oldText[i] != newText[i])
            return i;
    return -1;
}
static bool SequenceValid(Sequence sequence)
{
    return sequence.StartIndex >= 0 && sequence.EndIndex >= 0 && sequence.EndIndex >= sequence.StartIndex;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多