文本块内的索引无关字符比较

时间:2013-03-02 17:49:38

标签: c# string algorithm

我有以下任务:开发一个程序,其中有一个示例文本块,应该由用户输入。用户在测试期间做的任何拼写错误都被注册。基本上我可以将每个键入的char与基于示例的char进行比较但是在这种“天真”的方法中有一个重要的流程 - 如果用户错误地输入的字母多于整个字符串,或者在字符串之间插入的空格比应该更多,在这样的情况下其他比较将是错误的,因为额外的错误插入添加了索引偏移。我想到设计某种解析器,其中每个字符串(甚至一个字符)被标记化,并且比较是“char-wise” “而不是”索引方式“。但在我看来,这对于这样的任务来说就像是一种过度杀伤。我希望能够找到可能有助于解决这类问题的现有算法。

此外,我不确定这个问题是否更符合“程序员”网站的精神,所以我也将它发布在那里。

更新

我忘记提到的另一个重要细节是必须对每个输入进行评估,而不是在任务结束时,因为它包括输入时间记录等...

1 个答案:

答案 0 :(得分:0)

如果用户正在重新输入现有文本,那么它应该像对简单文本进行基于索引的比较一样简单(一个字符代表一个字母/数字等),或者您可以使用StringInfo.GetTextElementEnumerator作为示例。

如果您要求用户首先键入文本,然后检查发生了多少错误,您可以使用Levenshtein距离(请参阅此处了解实施和说明:http://www.dotnetperls.com/levenshtein)。 Levenshtein距离基本上是使一个字符串看起来像另一个字符串所需的编辑量。

请注意,如果您的任务是支持使用组合字符和代理对的unicode,其中需要2个或更多字符来表示一个字母,那么实现将在技术上不完整。在后一种情况下,您可以修改该实现以使用StringInfo.GetTextElementEnumerator生成和比较文本元素,如下所示:

using System;
using System.Collections.Generic;
using System.Globalization;

/// <summary>
///     Contains approximate string matching
/// </summary>
internal static class LevenshteinDistance
{
    /// <summary>
    ///     Compute the distance between two strings using text elements.
    /// </summary>
    public static int ComputeByTextElements(string s, string t) {
        string[] strA = GetTextElements(s),
                 strB = GetTextElements(t);

        int n = strA.Length;
        int m = strB.Length;
        var d = new int[n + 1,m + 1];

        // Step 1
        if (n == 0) {
            return m;
        }

        if (m == 0) {
            return n;
        }

        // Step 2
        for (int i = 0; i <= n; d[i, 0] = i++) {}

        for (int j = 0; j <= m; d[0, j] = j++) {}

        // Step 3
        for (int i = 1; i <= n; i++) {
            //Step 4
            for (int j = 1; j <= m; j++) {
                // Step 5
                int cost = (strB[j - 1] == strA[i - 1]) ? 0 : 1;

                // Step 6
                d[i, j] = Math.Min(
                    Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
                    d[i - 1, j - 1] + cost);
            }
        }
        // Step 7
        return d[n, m];
    }

    private static string[] GetTextElements(string str) {
        if (string.IsNullOrEmpty(str)) {
            return new string[] {};
        }

        var result = new List<string>();

        var enumerator = StringInfo.GetTextElementEnumerator(str);

        while (enumerator.MoveNext()) {
            result.Add(enumerator.Current.ToString());
        }

        return result.ToArray();
    }
}

另请注意,上述解决方案区分大小写,因此如果您需要不区分大小写的Levenshtein距离计算,您可能希望将输入全部为大写或小写。


关于您的修改

我不满足于基于(字符)索引的比较,因为它没有考虑组合字符和代理对。我将建立一个表示要输入的文本的文本元素数组,然后按键创建一个数组(或通过一些缓存实现进行比较的子集,以优化速度)输入的文本元素并进行比较。 / p>