【问题标题】:Pattern matching Paths+Files (UNC?)模式匹配路径+文件 (UNC?)
【发布时间】:2014-04-08 21:22:54
【问题描述】:

我正在使用 C# 和 Visual Studio 2010。我只是想匹配一个字符串(在这种情况下是一个路径)并创建一个模式来帮助我确定它是否是一个有效的模式。以下示例是任意组成的,但它们确实包含

所以我正在尝试创建一个与作为字符串传入的 UNC 路径匹配的模式。例如:

"\\\\Apple-butter27\\AliceFakePlace\\SomeDay\\Grand100\\Some File Name Stuff\\Yes these are fake words\\One more for fun2000343\\myText.txt"

以上是我尝试进行模式匹配的文件路径示例。我正在尝试将其与此模式匹配:

@"\\\\[a-zA-Z0-9-]+\\\w+\\\w+\\\w+\\((\w+)*(\s+)*)*\\((\w+)*(\s+)*)*\\((\w+)*(\s+)*)*\\w+\.txt";

我保证在我找到我的文件之前会有 7 个文件夹。我将不得不为几乎所有的段寻找空格、字母和数字的组合。

我确实尝试从匹配小的部分开始,例如我的第一次测试迭代我尝试将其作为我的模式:

@"\\\\";

这很有效,因为它会匹配前几个字符,但如果我添加它:

@"\\\\[a-zA-Z0-9-]+";

失败了。所以我想也许是因为字符串导致它加倍,所以我可能不得不加倍我的“\”所以我单独用 8 个“\”再次尝试,但失败了。

我之前的模式的目标是匹配 "\\\\Apple-butter27"

我一直在 google 和整个网站上查找,但我发现的与 UNC 匹配的模式都不是我的问题。

如果有人能告诉我这种模式有什么问题,我将不胜感激。至少是一个起点,因为我知道它很长,而且可能会是一个非常复杂的起点……但如果有人能指出它的一般问题。

虽然它是非字符串状态的路径,但看起来像这样:

\\Apple-butter27\AliceFakePlace\SomeDay\Grand100\Some File Name Stuff\Yes these are fake words\One more for fun2000343\myText.txt

我是尝试使用 UNC 路径进行模式匹配的新手,所以这开始让我很困惑,所以如果有人能指路,我将不胜感激。

我正在使用 Regex 的 .Success 函数来查看模式是否匹配,如果匹配成功或失败,我只是打印一条消息。我的主要关注点是模式,除非对将路径作为解决方案的字符串以外的东西使用有一些很好的见解。

【问题讨论】:

  • 我建议不要总是假设 Regex 是解决所有问题的最佳工具!由于您的假设,这是XY Problem
  • 我认为 Regex 是我的解决方案。我想验证一条路径是否以某种方式存在,如果不是,那么我希望它停止沿着某个文件路径向下移动并继续向其他路径查找它想要的信息。

标签: c# regex pattern-matching unc


【解决方案1】:

不需要正则表达式

或者,使用 System.Uri 类的内置解析:

foreach (var path in new [] { @"C:\foo\bar\", @"\\server\bar" })
{
    var uri = new Uri(path);

    if (uri.IsUnc)
    {
        Console.WriteLine("Connects to host '{0}'", uri.Host);
    }
    else
    {
        Console.WriteLine("Local path");
    }
}

打印:

本地路径
连接到主机“服务器”

如果你想匹配扩展,不要重新发明轮子,使用Path.GetExtension

var path = "\\some\really long and complicated path\foo.txt";
var extensionOfPath = Path.GetExtension(path);

if (string.Equals(".txt", extensionOfPath, StringComparison.CurrentCultureIgnoreCase))
{
    Console.WriteLine("It's a txt");
}
else
{
    Console.WriteLine("It's a '{0}', which is not a txt", extensionOfPath);
}

一般来说,我试图建议您在解决问题时避免跳到正则表达式。首先问问自己是否有人为您解决了问题 (example for HTML)。关于为什么正则表达式在CodingHorror 和(不太严重)on xkcd 上的代表不好的原因有很好的讨论。

正则表达式版本

如果您一心想使用 Regex,我认为这不是工作的最佳工具,它可以完成。使用间距和 cmets 确保您的代码可读。

string input = @"\\Apple-butter27\AliceFakePlace\SomeDay\Grand100\Some File Name Stuff\Yes these are fake words\One more for fun2000343\myText.txt";
Regex regex = new Regex(@"
    ^
    (?:
        # if server is present, capture to a named group
        # use a noncapturing group to remove the surrounding slashes
        # * is a greedy match, so it will butt up against the following directory search
        # this group may or may not occur, so we allow either this or the drive to match (|)
        (?:\\\\(?<server>[^\\]*)\\)
        # if there is no server, then we best have a drive letter
        |(?:(?<drive>[A-Z]):\\)
    )
    # then we have a repeating group (+) to capture all the directory components
    (?:
        # each directory is composed of a name (which does not contain \\)
        # followed by \\
        (?<directory>[^\\]*)\\
    )+
    # then we have a file name, which is identifiable as we already ate the rest of
    # the string.  So, it is just all non-\\ characters at the end.
    (?<file>[^\\]*)
    $", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);

var matches = regex.Match(input).Groups;

foreach (var group in regex.GetGroupNames())
{
    Console.WriteLine("Matched {0}:", group);
    foreach (var value in matches[group].Captures.Cast<Capture>())
    {
        Console.WriteLine("\t{0}", value.Value);
    }
}

打印

Matched server:
        Apple-butter27
Matched drive:
Matched directory:
        AliceFakePlace
        SomeDay
        Grand100
        Some File Name Stuff
        Yes these are fake words
        One more for fun2000343
Matched file:
        myText.txt

我现在只是猜测......

听起来你有某种应用程序,它调用它所在的目录并在其下构建多层结构。类似于以下内容:

C:\
  root directory for the application\
    site name\
      date of work\
        project name\
          bar\
            actual.txt
            files.txt

你是否在寻找实际的文件,我不知道。无论哪种方式,我们都知道C:\root directory\ 并认为它可能有实际文件。然后我们可以获取目录树并枚举以找到实际的文件:

var diRoot = new DirectoryInfo(@"C:\drop");

var projectDirectories = FindProjects(diRoot);

// get all of the files in all of the project directories of type .txt
var projectFiles = projectDirectories.SelectMany(di => di.GetFiles("*.txt"));

// projectFiles now contains:
//  actual.txt
//  files.txt

private static IEnumerable<DirectoryInfo> FindProjects(DirectoryInfo cDir, int depth = 0)
{
    foreach (var di in cDir.GetDirectories())
    {
        // assume projects are three levels deep
        if (depth == 3)
        {
            // it's a project, so we can return it
            yield return di;
        }
        else
        {
            // pass it through, return the results
            foreach (var d in FindProjects(di, depth + 1))
                yield return d;
        }
    }
}

而且由于我们不对路径进行字符串操作,因此我们可以透明地处理本地和 UNC 路径。

【讨论】:

  • 我不介意文件夹是否存在,我已经检查过了。我正在做一堆文件夹潜水,我可以很容易地做到这一点,但我想尝试通过切断一些路径来减少时间,比如说“如果不是这样,那就不要打扰调查它”。所以文件夹有一个命名约定,但我最终只是想切断一些不必要的文件夹潜水。我相信正则表达式是解决方案,因为否则我不知道你会如何做类似“如果不是这样就不要这样做”这样的事情。
  • 恐怕我不明白你在做什么。 UriPath 都不需要或检查磁盘上是否存在文件,正则表达式也不需要。当您匹配常规字符串(例如:tmp314 -> tmp\d{3})时,正则表达式很有价值,用户生成的路径除了是常规的。
  • 我有基于msdn.microsoft.com/en-us/library/bb513869.aspx 的代码,用于处理文件内容。我需要检查路径中有一些东西,这就是我使用正则表达式的原因。我没有用它来查找文件,我希望用它来说“不,如果这样的话,不要去那里”。这就是正则表达式的目标。
  • 我了解如何枚举目录树,但我不明白您要做什么。 “我没有用它来做任何关于查找文件的事情”与“如果这样的话,不要去那里(可能是枚举中的潜在文件)”
  • 这是一个复杂的需求。我想我可以用它来设定一个条件,即不访问某些文件夹,并且我需要一些路径中的一些信息。但是我正在处理很多文件夹,并且不会有特定的名称,只有特定的格式可以让我知道我想要什么。所以我认为正则表达式是特定格式的发现者是关键。我现在在想,担心它是一件很头疼的事情,所以我现在只让它搜索目录中的所有文件夹,直到我想出别的东西来节省时间,然后找到那个路径信息部分。跨度>
【解决方案2】:

如果您尝试检查路径是否存在,您可以执行以下操作:

FileInfo fi = new FileInfo(@""\\\\Apple-butter27\\AliceFakePlace\\SomeDay\\Grand100\\Some File Name Stuff\\Yes these are fake words\\One more for fun2000343\\myText.txt"");
bool exists = fi.Exists;

但如果您在运行验证时无法访问这些路径,则可以使用此模式查找 \\Apple-butter27:

const string rootPattern = @"(\\\\[a-zA-Z-_0-9]+)";

const RegexOptions regexOptions = RegexOptions.Compiled;

var regex = new Regex(rootPattern, regexOptions);

            foreach (Match match in regex.Matches(fileName))
            {
                if (match.Success && match.Groups.Count >= 1 )
                {
                    shareRoot = match.Groups[0].Value;
                }
            }

我尝试了这种模式,第 0 组正好给了我 \\Apple-butter27 您必须在 [括号] 中添加您可能遇到的其他字符,例如可能是 '.'。

【讨论】:

    【解决方案3】:

    虽然我不能不同意 System.Uri 的使用(这可能是您需要的工具);我假设我们严格需要遵守模式匹配正则表达式:

            const string tString = "\\\\Apple-butter27\\AliceFakePlace\\SomeDay\\Grand100\\Some File Name Stuff\\Yes these are fake words\\One more for fun2000343\\myText.txt";
            const string tRegexPattern = @"(\\\\)?((?<Folder>[a-zA-Z0-9- ]+)(\\))";
            const RegexOptions tRegexOptions = RegexOptions.Compiled;
    
            Regex tRegex = new Regex(tRegexPattern, tRegexOptions);
    
            Console.WriteLine(tString);
    
            if (tRegex.Matches(tString).Count == 7)
            {
                foreach (Match iMatch in tRegex.Matches(tString))
                {
                    if (iMatch.Success && iMatch.Groups["Folder"].Length > 0)
                    {
                        Console.WriteLine(iMatch.Groups["Folder"].Value);
                    }
                }
            }
            else
                throw new Exception("String did not have a path of depth 7");
    

    虽然您可以强制正则表达式仅匹配 7 个组,但正则表达式实际上是为模式匹配而设计的,而不是“循环逻辑”。

    ? 组仅在后跟分隔符(尾随 '\')时匹配,因此它仅匹配文件夹模式,而不匹配文件或文件扩展名。

    【讨论】:

    • 如果你有人命名一个带有下划线的文件,或者一个带有句点的目录,或者实际上是名称中允许的其他数千个字符中的任何一个,这将失败。别介意无法本地化这样的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2014-02-21
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-16
    相关资源
    最近更新 更多