【问题标题】:Regex for parsing directory and filename用于解析目录和文件名的正则表达式
【发布时间】:2021-08-09 13:30:23
【问题描述】:

我正在尝试编写一个正则表达式,它将使用匹配组解析出完全限定路径的目录和文件名

所以...

/var/log/xyz/10032008.log

会识别group 1 to be "/var/log/xyz"group 2 to be "10032008.log"

看起来很简单,但我无法让匹配组为我的生活工作。

注意:正如一些受访者指出的那样,这可能不是正则表达式的好用处。一般来说,我更喜欢使用我正在使用的语言的文件 API。我实际上想做的事情比这要复杂一点,但解释起来会困难得多,所以我选择了一个每个人都熟悉的领域,以便最简洁地描述根本问题。

【问题讨论】:

    标签: regex parsing


    【解决方案1】:

    试试这个:

    ^(.+)\/([^\/]+)$
    

    编辑:转义正斜杠以防止在复制/粘贴正则表达式时出现问题

    【讨论】:

    • 你不想让它变得不贪婪(如果这个匿名正则表达式可以处理它),这样它就不必一直回溯到斜线吗?
    • 这个假设有一个路径,而不仅仅是一个文件名。
    • 当前目录(.)和根目录(/)也有问题。前者不是问题(完全限定的路径名​​不以点开头);后者可能是。正则表达式也不处理 .. 反向遍历 - 这可能没问题,因为完全限定可能意味着没有点点位。
    • ^(.+)\/([^\/]+)$ 正斜杠必须转义?
    • 你需要避开前斜线,否则这个答案正是我在 Answers.Splunk.com 上尝试解决这个问题时所需要的 - answers.splunk.com/answers/777810/…
    【解决方案2】:

    在支持带有non-capturing groups 的正则表达式的语言中:

    ((?:[^/]*/)*)(.*)
    

    我将通过分解它来解释粗糙的正则表达式......

    (
      (?:
        [^/]*
        /
      )
      *
    )
    (.*)
    

    各部分的含义:

    (  -- capture group 1 starts
      (?:  -- non-capturing group starts
        [^/]*  -- greedily match as many non-directory separators as possible
        /  -- match a single directory-separator character
      )  -- non-capturing group ends
      *  -- repeat the non-capturing group zero-or-more times
    )  -- capture group 1 ends
    (.*)  -- capture all remaining characters in group 2
    

    示例

    为了测试正则表达式,我使用了以下 Perl 脚本...

    #!/usr/bin/perl -w
    
    use strict;
    use warnings;
    
    sub test {
      my $str = shift;
      my $testname = shift;
    
      $str =~ m#((?:[^/]*/)*)(.*)#;
    
      print "$str -- $testname\n";
      print "  1: $1\n";
      print "  2: $2\n\n";
    }
    
    test('/var/log/xyz/10032008.log', 'absolute path');
    test('var/log/xyz/10032008.log', 'relative path');
    test('10032008.log', 'filename-only');
    test('/10032008.log', 'file directly under root');
    

    脚本的输出...

    /var/log/xyz/10032008.log -- absolute path
      1: /var/log/xyz/
      2: 10032008.log
    
    var/log/xyz/10032008.log -- relative path
      1: var/log/xyz/
      2: 10032008.log
    
    10032008.log -- filename-only
      1:
      2: 10032008.log
    
    /10032008.log -- file directly under root
      1: /
      2: 10032008.log
    

    【讨论】:

      【解决方案3】:

      大多数语言都具有路径解析功能,可以为您提供这些功能。如果您有能力,我建议您免费使用开箱即用的东西。

      假设 / 是路径分隔符...

      ^(.*/)([^/]*)$
      

      第一组是目录/路径信息,第二组是文件名。例如:

      • /foo/bar/baz.log:“/foo/bar/”是路径,“baz.log”是文件
      • foo/bar.log:“foo/”是路径,“bar.log”是文件
      • /foo/bar:“/foo/”是路径,“bar”是文件
      • /foo/bar/:“/foo/bar/”是路径,没有文件。

      【讨论】:

        【解决方案4】:

        什么语言?为什么要使用正则表达式来完成这个简单的任务?

        如果您必须

        ^(.*)/([^/]*)$
        

        给你你想要的两个部分。您可能需要引用括号:

        ^\(.*\)/\([^/]*\)$
        

        取决于您的首选语言语法。

        但我建议您只使用您的语言的字符串搜索功能来查找最后一个“/”字符,然后在该索引上拆分字符串。

        【讨论】:

        • 许多框架(例如 .NET/Python)都有将文件名与路径分开的方法,无需手动搜索“/”字符。这很好,因为这些工具通常是独立于平台的。
        • 是的,但他还没有指定语言。如果是 Python,我会建议 os.path.dirname 和 os.path.basename 。
        【解决方案5】:

        推理:

        我通过试错法做了一些研究。发现键盘中可用的所有值都可以作为文件或目录,除了 *nux 机器中的“/”。

        我使用 touch 命令为以下字符创建文件,它创建了一个文件。

        (下面的逗号分隔值)
        '!', '@', '#', '$', "'", '%', '^', '&', '*', '(', ')', '', '"' , '\', '-', ',', '[', ']', '{', '}', '`', '~', '>', '

        只有当我尝试创建'/'(因为它是根目录)和文件名容器/ 时它才失败,因为它是文件分隔符。

        当我做touch .时,它改变了当前目录.的修改时间。但是,file.log 是可能的。

        当然,a-zA-Z0-9-(hypen)、_(下划线)应该可以工作。

        结果

        因此,通过上述推理,我们知道文件名或目录名可以包含除/ 正斜杠之外的任何内容。因此,我们的正则表达式将由文件名/目录名中不存在的内容派生。

        /(?:(?P<dir>(?:[/]?)(?:[^\/]+/)+)(?P<filename>[^/]+))/
        

        逐步正则表达式创建过程

        模式说明

        第一步:从匹配root目录开始

        一个目录可以以/ 开头,当它是绝对路径和目录名时,它是相对的。因此,寻找出现零次或一次的/

        /(?P<filepath>(?P<root>[/]?)(?P<rest_of_the_path>.+))/
        

        Step-2:尝试找到第一个目录。

        接下来,一个目录和它的子目录总是用/分隔。目录名称可以是除/ 之外的任何名称。那就先匹配 /var/ 吧。

        /(?P<filepath>(?P<first_directory>(?P<root>[/]?)[^\/]+/)(?P<rest_of_the_path>.+))/
        

        Step-3:获取文件的完整目录路径

        接下来,让我们匹配所有目录

        /(?P<filepath>(?P<dir>(?P<root>[/]?)(?P<single_dir>[^\/]+/)+)(?P<rest_of_the_path>.+))/
        

        这里,single_dir 是yz/,因为它首先匹配var/,然后它找到下一次出现的相同模式,即log/,然后它找到下一次出现相同模式yz/。所以,它显示了模式的最后一次出现。

        第四步:匹配文件名并清理

        现在,我们知道我们永远不会使用像 single_dir、filepath、root 这样的组。因此,让我们清理一下。

        让我们将它们保持为组,但不要捕获这些组。

        而rest_of_the_path 只是文件名!所以,重命名。而且文件名中不会有/,所以最好保留[^/]

        /(?:(?P<dir>(?:[/]?)(?:[^\/]+/)+)(?P<filename>[^/]+))/
        

        这将我们带到最终结果。当然,还有其他几种方法可以做到。我只是在这里提到一种方式。

        这里列出了上面使用的正则表达式规则

        ^ 表示字符串以
        (?P&lt;dir&gt;pattern) 开头表示按组名捕获组。我们有两个组,组名dirfile
        (?:pattern) 表示不考虑该组或非捕获组。
        ? 表示匹配零或一。 + 表示匹配一个或多个 [^\/] 表示匹配除正斜杠以外的任何字符 (/)

        [/]? 表示如果它是绝对路径,那么它可以以 / 开头,否则它不会。因此,匹配零次或一次出现的/

        [^\/]+/ 表示一个或多个不是正斜杠 (/) 后跟正斜杠 (/) 的字符。这将匹配 var/xyz/。一次一个目录。

        【讨论】:

        • 源自 *nix 环境的大多数(如果不是全部)文件系统中的文件/目录名称接受除 '/' 和 '\0' 之外的所有字节值。
        【解决方案6】:

        这个呢?

        [/]{0,1}([^/]+[/])*([^/]*)
        

        确定性:

        ((/)|())([^/]+/)*([^/]*)
        

        严格:

        ^[/]{0,1}([^/]+[/])*([^/]*)$
        ^((/)|())([^/]+/)*([^/]*)$
        

        【讨论】:

          【解决方案7】:

          一个很晚的答案,但希望这会有所帮助

          ^(.+?)/([\w]+\.log)$
          

          这对/使用了惰性检查,我刚刚修改了接受的答案

          http://regex101.com/r/gV2xB7/1

          【讨论】:

            【解决方案8】:

            试试这个:

            /^(\/([^/]+\/)*)(.*)$/
            

            不过,它会在路径上留下斜杠。

            【讨论】:

              【解决方案9】:

              给出一个上传文件夹 URL 示例:

              https://drive.google.com/drive/folders/14Q6d-KiwgTKE-qm5EOZvHeX86-Wf9Q5f?usp=sharing
              

              正则表达式模式为:

              [-\w]{25,}   
              

              此模式也适用于 Google 表格以及 Excel 中的自定义函数:

              =REGEXEXTRACT(N2,"[-\w]{25,}")
              

              结果是:14Q6d-KiwgTKE-qm5EOZvHeX86-Wf9Q5f

              【讨论】:

              • 请花更多时间来创建更清晰的答案。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2010-12-13
              • 1970-01-01
              • 1970-01-01
              • 2013-02-13
              • 1970-01-01
              • 2010-09-08
              • 2022-01-25
              相关资源
              最近更新 更多