Как проверить, соответствует ли имя файла шаблону подстановки?

У меня есть шаблон шаблона, возможно, "*.txt" или "POS??.dat".

У меня также есть список имен файлов в памяти, которые мне нужно сравнить с этим шаблоном.

Как бы я это сделал, имея в виду, мне нужна точно такая же семантика, которую использует IO.DirectoryInfo.GetFiles(pattern).

РЕДАКТИРОВАТЬ: Слепой перевод этого в регулярное выражение НЕ будет работать.

Ответ 1

У меня есть полный ответ в коде для вас, что 95%, как FindFiles(string).

В 5%, что не существует коротких имен/длинных имен, во второй заметке в документации MSDN для этого функция.

Если вы все равно хотите получить это поведение, вам нужно будет завершить вычисление краткого имени каждой строки, имеющейся во входном массиве, а затем добавить длинное имя в коллекцию совпадений, если либо длинный или короткое имя соответствует шаблону.

Вот код:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace FindFilesRegEx
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] names = { "hello.t", "HelLo.tx", "HeLLo.txt", "HeLLo.txtsjfhs", "HeLLo.tx.sdj", "hAlLo20984.txt" };
            string[] matches;
            matches = FindFilesEmulator("hello.tx", names);
            matches = FindFilesEmulator("H*o*.???", names);
            matches = FindFilesEmulator("hello.txt", names);
            matches = FindFilesEmulator("lskfjd30", names);
        }

        public string[] FindFilesEmulator(string pattern, string[] names)
        {
            List<string> matches = new List<string>();
            Regex regex = FindFilesPatternToRegex.Convert(pattern);
            foreach (string s in names)
            {
                if (regex.IsMatch(s))
                {
                    matches.Add(s);
                }
            }
            return matches.ToArray();
        }

        internal static class FindFilesPatternToRegex
        {
            private static Regex HasQuestionMarkRegEx   = new Regex(@"\?", RegexOptions.Compiled);
            private static Regex IllegalCharactersRegex  = new Regex("[" + @"\/:<>|" + "\"]", RegexOptions.Compiled);
            private static Regex CatchExtentionRegex    = new Regex(@"^\s*.+\.([^\.]+)\s*$", RegexOptions.Compiled);
            private static string NonDotCharacters      = @"[^.]*";
            public static Regex Convert(string pattern)
            {
                if (pattern == null)
                {
                    throw new ArgumentNullException();
                }
                pattern = pattern.Trim();
                if (pattern.Length == 0)
                {
                    throw new ArgumentException("Pattern is empty.");
                }
                if(IllegalCharactersRegex.IsMatch(pattern))
                {
                    throw new ArgumentException("Pattern contains illegal characters.");
                }
                bool hasExtension = CatchExtentionRegex.IsMatch(pattern);
                bool matchExact = false;
                if (HasQuestionMarkRegEx.IsMatch(pattern))
                {
                    matchExact = true;
                }
                else if(hasExtension)
                {
                    matchExact = CatchExtentionRegex.Match(pattern).Groups[1].Length != 3;
                }
                string regexString = Regex.Escape(pattern);
                regexString = "^" + Regex.Replace(regexString, @"\\\*", ".*");
                regexString = Regex.Replace(regexString, @"\\\?", ".");
                if(!matchExact && hasExtension)
                {
                    regexString += NonDotCharacters;
                }
                regexString += "$";
                Regex regex = new Regex(regexString, RegexOptions.Compiled | RegexOptions.IgnoreCase);
                return regex;
            }
        }
    }
}

Ответ 2

Вы можете просто сделать это. Вам не нужны регулярные выражения.

using Microsoft.VisualBasic.CompilerServices;

if (Operators.LikeString("pos123.txt", "pos?23.*", CompareMethod.Text))
{
  Console.WriteLine("Filename matches pattern");
}

Или, в VB.Net,

If "pos123.txt" Like "pos?23.*" Then
  Console.WriteLine("Filename matches pattern")
End If

В С# вы можете имитировать это с помощью метода расширения. Это было бы не так, как VB Like, но это было бы... очень круто.

Ответ 3

Вы можете перевести подстановочные знаки в регулярное выражение:

*.txt -> ^.+\.txt$

POS??.dat _> ^POS..\.dat$

Используйте метод Regex.Escape, чтобы избежать символов, которые не являются символами wildcars, в литеральные строки для шаблона (например, преобразование ".txt" в "\.txt").

Подстановочный знак * преобразуется в .+, а ? преобразуется в .

Поместите ^ в начало шаблона, чтобы он соответствовал началу строки, а $в конце соответствовал концу строки.

Теперь вы можете использовать метод Regex.IsMatch, чтобы проверить, соответствует ли имя файла шаблону.

Ответ 4

Какой-то тип regex/glob - путь, но есть некоторые тонкости; ваш вопрос указывает, что вы хотите идентичную семантику IO.DirectoryInfo.GetFiles. Это может быть проблемой из-за особых случаев, связанных с именами файлов 8.3 и длинными именами и т.п. Вся история находится на MSDN.

Если вам не требуется точное поведенческое совпадение, есть несколько хороших вопросов SO:

сопоставление шаблонов glob в .NET
Как реализовать glob в С#

Ответ 5

Plz попробуйте приведенный ниже код.

static void Main(string[] args)
    {
        string _wildCardPattern = "*.txt";

        List<string> _fileNames = new List<string>();
        _fileNames.Add("text_file.txt");
        _fileNames.Add("csv_file.csv");

        Console.WriteLine("\nFilenames that matches [{0}] pattern are : ", _wildCardPattern);
        foreach (string _fileName in _fileNames)
        {
            CustomWildCardPattern _patetrn = new CustomWildCardPattern(_wildCardPattern);
            if (_patetrn.IsMatch(_fileName))
            {
                Console.WriteLine("{0}", _fileName);
            }
        }

    }

public class CustomWildCardPattern : Regex
{
    public CustomWildCardPattern(string wildCardPattern)
        : base(WildcardPatternToRegex(wildCardPattern))
    {
    }

    public CustomWildCardPattern(string wildcardPattern, RegexOptions regexOptions)
        : base(WildcardPatternToRegex(wildcardPattern), regexOptions)
    {
    }

    private static string WildcardPatternToRegex(string wildcardPattern)
    {
        string patternWithWildcards = "^" + Regex.Escape(wildcardPattern).Replace("\\*", ".*");
        patternWithWildcards = patternWithWildcards.Replace("\\?", ".") + "$";
        return patternWithWildcards;
    }
}

Ответ 6

Для тех, кто сталкивается с этим вопросом сейчас, когда это было много лет спустя, я нашел в социальных советах MSDN, что метод GetFiles() примет * и? подстановочные знаки в параметре searchPattern. (По крайней мере, в .Net 3.5, 4.0 и 4.5)

Directory.GetFiles(string path, string searchPattern)

http://msdn.microsoft.com/en-us/library/wz42302f.aspx

Ответ 7

Просто используйте класс Regex. Инициализируйте его с шаблоном подстановочных знаков, о котором вы думаете, а затем используйте метод .IsMatch(filename), чтобы проверить каждое имя файла, чтобы увидеть, совпадает ли он.

Ответ 8

Использование RegexOptions.IgnoreCase будет исправлять его.

public class WildcardPattern : Regex {
    public WildcardPattern(string wildCardPattern)
        : base(ConvertPatternToRegex(wildCardPattern), RegexOptions.IgnoreCase) {
    }

    public WildcardPattern(string wildcardPattern, RegexOptions regexOptions)
        : base(ConvertPatternToRegex(wildcardPattern), regexOptions) {
    }

    private static string ConvertPatternToRegex(string wildcardPattern) {
        string patternWithWildcards = Regex.Escape(wildcardPattern).Replace("\\*", ".*");
        patternWithWildcards = string.Concat("^", patternWithWildcards.Replace("\\?", "."), "$");
        return patternWithWildcards;
    }
}