2009-03-16 7 views
24

Mam wzorzec wieloznaczny, być może "* .txt" lub "POS ??. Dat".Jak sprawdzić, czy nazwa pliku pasuje do wzorca wieloznacznego?

Mam również listę nazw plików w pamięci, które muszę porównać z tym wzorcem.

Jak to zrobić, pamiętając, że potrzebuję dokładnie tej samej semantyki, którą używa IO.DirectoryInfo.GetFiles (pattern).

EDYCJA: Ślepo przetłumaczyć to na wyrażenie regularne NIE będzie działać.

+0

Dla każdego, kto napotkał to pytanie teraz, gdy minęło wiele lat, znalazłem na forum społecznościowym MSDN, że metoda GetFiles() zaakceptuje * i? znaki wieloznaczne w parametrze searchPattern. (Przynajmniej w .NET 3.5, 4.0 i 4.5) Directory.GetFiles (ścieżki ciąg, ciąg searchPattern) http://msdn.microsoft.com/en-us/library/wz42302f.aspx – jgerman

Odpowiedz

40

Mam dla Ciebie pełną odpowiedź w 95%, np. FindFiles(string).

5%, którego nie ma, jest zachowanie krótkich nazw/długich nazw w drugiej notatce w dokumentacji MSDN dla tej funkcji.

Jeśli nadal chcesz uzyskać takie zachowanie, musisz wykonać obliczenie krótkiej nazwy każdego łańcucha, który masz w tablicy wejściowej, a następnie dodać długą nazwę do kolekcji dopasowań, jeśli długa lub krótka nazwa pasuje do wzorca.

Oto kod:

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

Miło, wyraźnie wkładasz w to sporo myśli. –

+1

Świetny kawałek kodu. Zmienna HasAsteriskRegex nigdy nie jest używana. –

+0

@Dor Rotman, dzięki ... Poprawiłem odpowiednio kod.Prawdopodobnie początkowo myślałem, że będę musiał sprawdzić gwiazdkę pod kątem poprawności wzoru i zapomniałem później usunąć nieużywany Regex. – sprite

-2

Po prostu użyj klasy Regex. Zainicjuj go za pomocą wzoru wieloznacznego, o którym myślisz, a następnie użyj metody .IsMatch (filename), aby sprawdzić każdą nazwę pliku, aby sprawdzić, czy pasuje.

+0

większość Wyrażenia regularne używają inna składnia niż typowa globalizacja powłoki, o której mówi OP. Nie wiem, czy tak jest w .net czy nie. – rmeador

+0

Nie można po prostu na ślepo przetłumaczyć wzorców wieloznacznych na wyrażenia regularne. –

+0

Właściwie, zdawałem sobie sprawę, że nie można tłumaczyć wzorców wieloznacznych na wyrażenie regularne. Po prostu założyłem, że OP zrozumie, że musi przetłumaczyć * .txt na równoważne wyrażenie, gdy użyje klasy regex. –

2

Można tłumaczyć symboli wieloznacznych do wyrażenia regularnego:

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

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

Użyj metody Regex.Escape uciec znaki, które nie są wildcars język dosłowne ciągi dla wzoru (np konwersji ".txt" do "\.txt").

symbolu wieloznacznego * przekłada się .+ i ? przekłada się .

Put^na początku wzorca aby dopasować początek napisu, i $ na koniec, aby dopasować się do końca łańcucha. Teraz można użyć metody Regex.IsMatch, aby sprawdzić, czy nazwa pliku pasuje do wzorca

+0

-1, ponieważ ta odpowiedź jest po prostu błędna. Działa prawie dla dwóch opublikowanych przykładów, z tym że musisz upewnić się, że w regex nie ma znaczenia wielkość liter. Ale zachowanie GetFiles jest dość skomplikowane. Szczegółowe informacje można znaleźć na stronie http://msdn.microsoft.com/en-us/library/8he88b63.aspx. –

+0

Dzięki za próbę, ale tak jak powiedziałem, to musi dokładnie dopasować GetFiles dokładnie, a to nie będzie. –

+0

Cóż, nie można uzyskać takiego samego zachowania jak GetFiles z listą nazw plików, ponieważ nie można się dowiedzieć, jakie były krótkie nazwy plików. – Guffa

2

Jakiś rodzaj regex/glob jest do zrobienia, ale są pewne subtelności; twoje pytanie wskazuje, że chcesz semantykę identyczną z IO.DirectoryInfo.GetFiles. Może to stanowić wyzwanie, ze względu na szczególne przypadki dotyczące 8,3 w porównaniu z długimi nazwami plików i tym podobne. Cała historia jest na MSDN.

Jeśli nie potrzeba dokładnego dopasowania behawioralnej, istnieje kilka dobrych SO pytania:

glob pattern matching in .NET
How to implement glob in C#

0

Plz spróbować poniższy kod.

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; 
    } 
} 
+0

Ten działa lepiej niż odpowiedź sprite'a na * .txt, ale ten nie bierze pod uwagę wielkich i małych liter i nie pasował do * .txt? – Graham

+1

To zachowanie nie pasuje do tego w pytaniu. Poproszono o dopasowanie metody DirectoryInfo.GetFiles. – sprite

-2

Zastosowanie RegexOptions.IgnoreCase będzie to naprawić.

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; 
    } 
} 
+0

Czy odpowiada na pytanie? – FazoM

0

Dla każdego, kto jest po drugiej stronie to pytanie teraz, że jest to rok później znalazłem się w MSDN płyt społeczne, które GetFiles metoda() będzie akceptować * i? znaki wieloznaczne w parametrze searchPattern. (Przynajmniej w .NET 3.5, 4.0 i 4.5)

Directory.GetFiles(string path, string searchPattern) 

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

+0

Przepraszam, ale -1, ponieważ w ogóle nie odpowiedziałeś na pytanie. – sprite

+1

Odpowiedziałem na ograniczenie, które zostało stwierdzone w pytaniu, mówiąc, że nie jest już ograniczeniem. Po co wydawać własne punkty reputacji, aby ukarać kogoś, kto aktualizował stare pytanie z nowymi informacjami, ale kto nie miał jeszcze dość reputacji, aby edytować oryginalne pytanie podczas publikowania aktualizacji? – jgerman

+0

Ponieważ nie jest to odpowiedź, to, co napisałeś, powinno być komentarzem do pierwotnego pytania. Osoba, która wysłała lub moderatora, może następnie edytować. To nie jest osobiste. Jeśli chodzi o "marnowanie" mojej reputacji, nie interesuje mnie zbyt wiele, nie kłaniam się z nikim. – sprite

10

można po prostu to zrobić. Nie potrzebujesz wyrażeń regularnych.

using Microsoft.VisualBasic.CompilerServices; 

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

Lub, w VB.NET,

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

w C# można symulować to z metodę rozszerzenia. Nie byłby dokładnie taki jak VB Like, ale byłby ... bardzo fajny.

+0

Lub ... jeśli piszesz w VB.NET, możesz użyć operatora 'like' bezpośrednio! Piszę VB od ponad 15 lat i nigdy nie korzystałem z operatora 'like'. ** JEŻELI ** Wiedziałem, że operator istnieje, nigdy nie miałem potrzeby go używać. Mam zamiar go teraz użyć. – CrazyIvan1974

+0

@ CrazyIvan1974, dodałem twój pomysł. Dzięki! – toddmo

Powiązane problemy