正規表現以外のパターンに一致する .NET の組み込みメカニズムはありますか? UNIX スタイル (グロブ) のワイルドカード (* = 任意の文字の任意の数) を使用して一致させたいと考えています。
これをエンドユーザー向けのコントロールに使用したいと思います。すべての RegEx 機能を許可すると、非常に混乱するのではないかと心配しています。
私は自分のコードをもう少しセマンティックなものにしたいので、次の拡張メソッドを書きました。
using System.Text.RegularExpressions;
namespace Whatever
{
public static class StringExtensions
{
/// <summary>
/// Compares the string against a given pattern.
/// </summary>
/// <param name="str">The string.</param>
/// <param name="pattern">The pattern to match, where "*" means any sequence of characters, and "?" means any single character.</param>
/// <returns><c>true</c> if the string matches the given pattern; otherwise <c>false</c>.</returns>
public static bool Like(this string str, string pattern)
{
return new Regex(
"^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$",
RegexOptions.IgnoreCase | RegexOptions.Singleline
).IsMatch(str);
}
}
}
(名前空間を変更するか、拡張メソッドを独自の文字列拡張クラスにコピーします)
この拡張機能を使用すると、次のようなステートメントを記述できます。
if (File.Name.Like("*.jpg"))
{
....
}
コードをもう少し読みやすくするために砂糖を入れてください:-)
私はあなたのための実際のコードを見つけました:
Regex.Escape( wildcardExpression ).Replace( @"\*", ".*" ).Replace( @"\?", "." );
リスト メソッドの 2 引数と 3 引数のバリアントは、 と の両方を使用して、ファイル名のグロビングをサポートする 2 番目の引数として検索文字列を取りGetFiles()
ます。EnumerateDirectories()
*
?
class GlobTestMain
{
static void Main(string[] args)
{
string[] exes = Directory.GetFiles(Environment.CurrentDirectory, "*.exe");
foreach (string file in exes)
{
Console.WriteLine(Path.GetFileName(file));
}
}
}
譲るだろう
GlobTest.exe
GlobTest.vshost.exe
ドキュメントには、一致する拡張機能にはいくつかの注意事項があると記載されています。また、8.3 のファイル名が一致する (バックグラウンドで自動的に生成される可能性がある) ことも示されているため、特定のパターンで「重複した」一致が発生する可能性があります。
これをサポートするメソッドは、、、GetFiles()
およびGetDirectories()
ですGetFileSystemEntries()
。Enumerate
バリアントもこれをサポートしています。
If you use VB.Net, you can use the Like statement, which has Glob like syntax.
正規表現を避けたい場合、これは基本的な glob の実装です:
public static class Globber
{
public static bool Glob(this string value, string pattern)
{
int pos = 0;
while (pattern.Length != pos)
{
switch (pattern[pos])
{
case '?':
break;
case '*':
for (int i = value.Length; i >= pos; i--)
{
if (Glob(value.Substring(i), pattern.Substring(pos + 1)))
{
return true;
}
}
return false;
default:
if (value.Length == pos || char.ToUpper(pattern[pos]) != char.ToUpper(value[pos]))
{
return false;
}
break;
}
pos++;
}
return value.Length == pos;
}
}
次のように使用します。
Assert.IsTrue("text.txt".Glob("*.txt"));
Based on previous posts, I threw together a C# class:
using System;
using System.Text.RegularExpressions;
public class FileWildcard
{
Regex mRegex;
public FileWildcard(string wildcard)
{
string pattern = string.Format("^{0}$", Regex.Escape(wildcard)
.Replace(@"\*", ".*").Replace(@"\?", "."));
mRegex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
}
public bool IsMatch(string filenameToCompare)
{
return mRegex.IsMatch(filenameToCompare);
}
}
Using it would go something like this:
FileWildcard w = new FileWildcard("*.txt");
if (w.IsMatch("Doug.Txt"))
Console.WriteLine("We have a match");
The matching is NOT the same as the System.IO.Directory.GetFiles() method, so don't use them together.
C# からは、.NET のLikeOperator.LikeStringメソッドを使用できます。これは、VB のLIKE 演算子のバッキング実装です。*、?、#、[charlist]、および [!charlist] を使用したパターンをサポートします。
.NET Framework のすべてのバージョンに含まれている Microsoft.VisualBasic.dll アセンブリへの参照を追加することにより、C# から LikeString メソッドを使用できます。次に、他の静的 .NET メソッドと同様に、LikeString メソッドを呼び出します。
using Microsoft.VisualBasic;
using Microsoft.VisualBasic.CompilerServices;
...
bool isMatch = LikeOperator.LikeString("I love .NET!", "I love *", CompareMethod.Text);
// isMatch should be true.
https://www.nuget.org/packages/Glob.cs
https://github.com/mganss/Glob.cs
.NET 用の GNU Glob。
インストール後にパッケージ参照を取り除き、単一の Glob.cs ソース ファイルをコンパイルするだけです。
これは GNU Glob の実装であるため、別の同様の実装を見つけたらクロス プラットフォームおよびクロス言語になります。
I don't know if the .NET framework has glob matching, but couldn't you replace the * with .*? and use regexes?
好奇心から、私は Microsoft.Extensions.FileSystemGlobbing をちらりと見ました - そして、それは非常に多くのライブラリに非常に大きな依存関係を引きずっていました - 私はなぜ似たようなものを書こうとできないのかを決めました?
そうですね - 言うは易く行うは難しですが、結局のところ、それほど些細な機能ではないことにすぐに気付きました。フォルダ。
Microsoft は、"./*.txt" のようないくつかの奇妙な一致パターン シーケンスもテストしています。"./" の種類の文字列が実際に必要な人が誰なのかはわかりません。それらは処理中に削除されるためです。( https://github.com/aspnet/FileSystem/blob/dev/test/Microsoft.Extensions.FileSystemGlobbing.Tests/PatternMatchingTests.cs )
とにかく、私は自分の関数をコーディングしました - そしてそれの 2 つのコピーがあります - 1 つは svn にあります (後でバグ修正するかもしれません) - デモ目的でここにも 1 つのサンプルをコピーします。svn リンクからコピー ペーストすることをお勧めします。
SVN リンク:
https://sourceforge.net/p/syncproj/code/HEAD/tree/SolutionProjectBuilder.cs#l800 (正しくジャンプしない場合は、matchFiles 関数を検索してください)。
そして、ここにもローカル関数のコピーがあります:
/// <summary>
/// Matches files from folder _dir using glob file pattern.
/// In glob file pattern matching * reflects to any file or folder name, ** refers to any path (including sub-folders).
/// ? refers to any character.
///
/// There exists also 3-rd party library for performing similar matching - 'Microsoft.Extensions.FileSystemGlobbing'
/// but it was dragging a lot of dependencies, I've decided to survive without it.
/// </summary>
/// <returns>List of files matches your selection</returns>
static public String[] matchFiles( String _dir, String filePattern )
{
if (filePattern.IndexOfAny(new char[] { '*', '?' }) == -1) // Speed up matching, if no asterisk / widlcard, then it can be simply file path.
{
String path = Path.Combine(_dir, filePattern);
if (File.Exists(path))
return new String[] { filePattern };
return new String[] { };
}
String dir = Path.GetFullPath(_dir); // Make it absolute, just so we can extract relative path'es later on.
String[] pattParts = filePattern.Replace("/", "\\").Split('\\');
List<String> scanDirs = new List<string>();
scanDirs.Add(dir);
//
// By default glob pattern matching specifies "*" to any file / folder name,
// which corresponds to any character except folder separator - in regex that's "[^\\]*"
// glob matching also allow double astrisk "**" which also recurses into subfolders.
// We split here each part of match pattern and match it separately.
//
for (int iPatt = 0; iPatt < pattParts.Length; iPatt++)
{
bool bIsLast = iPatt == (pattParts.Length - 1);
bool bRecurse = false;
String regex1 = Regex.Escape(pattParts[iPatt]); // Escape special regex control characters ("*" => "\*", "." => "\.")
String pattern = Regex.Replace(regex1, @"\\\*(\\\*)?", delegate (Match m)
{
if (m.ToString().Length == 4) // "**" => "\*\*" (escaped) - we need to recurse into sub-folders.
{
bRecurse = true;
return ".*";
}
else
return @"[^\\]*";
}).Replace(@"\?", ".");
if (pattParts[iPatt] == "..") // Special kind of control, just to scan upper folder.
{
for (int i = 0; i < scanDirs.Count; i++)
scanDirs[i] = scanDirs[i] + "\\..";
continue;
}
Regex re = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
int nScanItems = scanDirs.Count;
for (int i = 0; i < nScanItems; i++)
{
String[] items;
if (!bIsLast)
items = Directory.GetDirectories(scanDirs[i], "*", (bRecurse) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
else
items = Directory.GetFiles(scanDirs[i], "*", (bRecurse) ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
foreach (String path in items)
{
String matchSubPath = path.Substring(scanDirs[i].Length + 1);
if (re.Match(matchSubPath).Success)
scanDirs.Add(path);
}
}
scanDirs.RemoveRange(0, nScanItems); // Remove items what we have just scanned.
} //for
// Make relative and return.
return scanDirs.Select( x => x.Substring(dir.Length + 1) ).ToArray();
} //matchFiles
バグが見つかった場合は、修正を惜しみません。