namespace Dpz.Core.Simple.Test;

/// <summary>
/// 通配符匹配调试测试
/// 用于调试和验证特定的匹配情况
/// </summary>
public class WildcardDebugTest
{
    [Test]
    public void DebugSpecificCases()
    {
        // 测试具体的问题案例
        TestCase("test", "t??t", false, "4个字符的字符串匹配4个字符的模式,但最后一个字符不匹配");
        TestCase("tast", "t??t", true, "4个字符匹配,第2、3位任意,第4位匹配");
        TestCase("", "*", true, "空字符串应该匹配星号");
        TestCase("", "", true, "空字符串应该匹配空字符串");
        TestCase("", "a", false, "空字符串不应该匹配非空模式");
        TestCase("a", "", false, "非空字符串不应该匹配空模式");

        // 更多边界测试
        TestCase("test", "????", true, "4个字符匹配4个问号");
        TestCase("test", "???", false, "4个字符不匹配3个问号");
        TestCase("test", "?????", false, "4个字符不匹配5个问号");

        // 混合测试
        TestCase("test.txt", "*.txt", true, "文件扩展名匹配");
        TestCase("test.doc", "*.txt", false, "文件扩展名不匹配");
        TestCase("a", "?", true, "单字符匹配单问号");
        TestCase("ab", "?", false, "两字符不匹配单问号");
    }

    private static void TestCase(string input, string pattern, bool expected, string description)
    {
        var result = WildcardMatch(input.AsSpan(), pattern.AsSpan());
        Console.WriteLine(
            $"测试: '{input}' vs '{pattern}' => {result} (预期: {expected}) - {description}"
        );

        if (result != expected)
        {
            Console.WriteLine(
                $"❌ 失败: 输入='{input}', 模式='{pattern}', 结果={result}, 预期={expected}"
            );
            // 让我们详细跟踪这个匹配过程
            DetailedTrace(input, pattern);
        }
        else
        {
            Console.WriteLine($"✅ 成功");
        }
        Console.WriteLine();
    }

    private static void DetailedTrace(string input, string pattern)
    {
        Console.WriteLine("=== 详细匹配过程 ===");
        var inputSpan = input.AsSpan();
        var patternSpan = pattern.AsSpan();

        Console.WriteLine($"输入长度: {inputSpan.Length}, 模式长度: {patternSpan.Length}");

        // 处理空字符串的特殊情况
        if (patternSpan.Length == 0)
        {
            Console.WriteLine("模式为空,返回输入是否也为空");
            return;
        }

        if (inputSpan.Length == 0)
        {
            Console.WriteLine("输入为空,检查模式是否全为 '*'");
            for (var i = 0; i < patternSpan.Length; i++)
            {
                Console.WriteLine($"模式[{i}] = '{patternSpan[i]}'");
                if (patternSpan[i] != '*')
                {
                    Console.WriteLine("发现非 '*' 字符,返回 false");
                    return;
                }
            }
            Console.WriteLine("模式全为 '*',返回 true");
            return;
        }

        var inputIndex = 0;
        var patternIndex = 0;
        var starIndex = -1;
        var match = 0;
        var step = 0;

        Console.WriteLine("开始主匹配循环:");
        while (inputIndex < inputSpan.Length)
        {
            step++;
            Console.WriteLine(
                $"步骤 {step}: inputIndex={inputIndex}, patternIndex={patternIndex}, starIndex={starIndex}, match={match}"
            );

            if (inputIndex < inputSpan.Length)
            {
                Console.WriteLine($"  当前输入字符: '{inputSpan[inputIndex]}'");
            }

            if (patternIndex < patternSpan.Length)
            {
                Console.WriteLine($"  当前模式字符: '{patternSpan[patternIndex]}'");
            }

            // 如果模式字符是 '?' 或者字符匹配
            if (
                patternIndex < patternSpan.Length
                && (
                    patternSpan[patternIndex] == '?'
                    || char.ToLowerInvariant(inputSpan[inputIndex])
                        == char.ToLowerInvariant(patternSpan[patternIndex])
                )
            )
            {
                Console.WriteLine("  匹配成功,两个索引都前进");
                inputIndex++;
                patternIndex++;
            }
            // 如果模式字符是 '*'
            else if (patternIndex < patternSpan.Length && patternSpan[patternIndex] == '*')
            {
                Console.WriteLine("  遇到 '*',记录位置并跳过");
                starIndex = patternIndex;
                match = inputIndex;
                patternIndex++;
            }
            // 如果之前遇到过 '*',回溯
            else if (starIndex != -1)
            {
                Console.WriteLine("  不匹配但有 '*',回溯");
                patternIndex = starIndex + 1;
                match++;
                inputIndex = match;
            }
            // 不匹配
            else
            {
                Console.WriteLine("  不匹配且无法回溯,返回 false");
                return;
            }
        }

        Console.WriteLine("主循环结束,处理模式末尾的 '*'");
        while (patternIndex < patternSpan.Length && patternSpan[patternIndex] == '*')
        {
            Console.WriteLine($"跳过模式末尾的 '*' at {patternIndex}");
            patternIndex++;
        }

        Console.WriteLine(
            $"最终结果: patternIndex={patternIndex}, pattern.Length={patternSpan.Length}"
        );
        Console.WriteLine($"返回: {patternIndex == patternSpan.Length}");
    }

    private static bool WildcardMatch(ReadOnlySpan<char> input, ReadOnlySpan<char> pattern)
    {
        // 处理空字符串的特殊情况
        if (pattern.Length == 0)
        {
            return input.Length == 0;
        }

        if (input.Length == 0)
        {
            // 输入为空,只有模式全部是 '*' 才匹配
            for (var i = 0; i < pattern.Length; i++)
            {
                if (pattern[i] != '*')
                {
                    return false;
                }
            }
            return true;
        }

        var inputIndex = 0;
        var patternIndex = 0;
        var starIndex = -1;
        var match = 0;

        while (inputIndex < input.Length)
        {
            // 如果模式字符是 '?' 或者字符匹配
            if (
                patternIndex < pattern.Length
                && (
                    pattern[patternIndex] == '?'
                    || char.ToLowerInvariant(input[inputIndex])
                        == char.ToLowerInvariant(pattern[patternIndex])
                )
            )
            {
                inputIndex++;
                patternIndex++;
            }
            // 如果模式字符是 '*'
            else if (patternIndex < pattern.Length && pattern[patternIndex] == '*')
            {
                starIndex = patternIndex;
                match = inputIndex;
                patternIndex++;
            }
            // 如果之前遇到过 '*',回溯
            else if (starIndex != -1)
            {
                patternIndex = starIndex + 1;
                match++;
                inputIndex = match;
            }
            // 不匹配
            else
            {
                return false;
            }
        }

        // 跳过模式末尾的 '*'
        while (patternIndex < pattern.Length && pattern[patternIndex] == '*')
        {
            patternIndex++;
        }

        return patternIndex == pattern.Length;
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个用于测试和调试通配符匹配算法的单元测试类。让我详细解释一下这个代码的结构和功能:

主要功能

这个类实现了一个通配符匹配算法,支持两种通配符:

  • *:匹配任意数量的字符(包括0个)
  • ?:匹配单个字符

代码结构分析

1. 主测试方法 DebugSpecificCases()

[Test]
public void DebugSpecificCases()

这是一个NUnit测试方法,包含了多个测试用例来验证通配符匹配的各种场景:

边界情况测试:

  • 空字符串与各种模式的匹配
  • 长度不匹配的情况

具体匹配测试:

  • "test" vs "t??t" → false(最后一个字符不匹配)
  • "tast" vs "t??t" → true(中间两位任意,首尾匹配)
  • 文件扩展名匹配等实际应用场景

2. 测试辅助方法 TestCase()

private static void TestCase(string input, string pattern, bool expected, string description)

这个方法负责:

  • 执行单个测试用例
  • 比较实际结果与期望结果
  • 输出测试结果和描述
  • 如果测试失败,调用详细跟踪方法

3. 详细跟踪方法 DetailedTrace()

private static void DetailedTrace(string input, string pattern)

当测试失败时,这个方法会:

  • 逐步显示匹配算法的执行过程
  • 显示每一步的索引位置和当前字符
  • 帮助开发者理解算法的执行逻辑和失败原因

4. 核心匹配算法 WildcardMatch()

private static bool WildcardMatch(ReadOnlySpan<char> input, ReadOnlySpan<char> pattern)

这是核心的通配符匹配算法,使用了回溯策略

算法逻辑:

  1. 边界处理:处理空字符串的特殊情况
  2. 主循环匹配
    • 如果字符直接匹配或遇到?:两个索引都前进
    • 如果遇到*:记录星号位置,准备回溯
    • 如果不匹配但之前有*:回溯到星号后继续尝试
    • 如果不匹配且无法回溯:返回false
  3. 后处理:跳过模式末尾的*
  4. 最终判断:检查是否完全匹配

关键变量:

  • inputIndex:当前输入字符串位置
  • patternIndex:当前模式字符串位置
  • starIndex:最近遇到的*的位置
  • match*匹配开始的输入位置

算法特点

  1. 高效性:使用ReadOnlySpan<char>避免字符串复制
  2. 大小写不敏感:使用char.ToLowerInvariant()进行比较
  3. 回溯机制:遇到*时能够智能回溯尝试不同的匹配长度
  4. 调试友好:提供详细的执行跟踪信息

这个代码非常适合用于文件名匹配、路径过滤等需要通配符支持的场景。

评论加载中...