【问题标题】:How to check if a file name contains directory info?如何检查文件名是否包含目录信息?
【发布时间】:2014-02-28 16:46:35
【问题描述】:

我想检查一个文件名中是否包含任何目录信息,最好不要使用像index($file_name,'/')!=-1 这样的系统相关黑客。我知道File::Spec 模块,但似乎找不到使用该模块做出此决定的方法。也许另一个模块会更好。以下是一些文件名示例:

# This does not have directory info, so the test should fail (i.e., return false)
my $file_name1='abc.txt';

# These do have directory info, so the test should succeed (i.e., return true)
my $file_name2='dir/abc.txt';
my $file_name3='/home/me/abc.txt';
my $file_name4='~me/abc.txt';

【问题讨论】:

  • 所以你想要一种便携的方式来做index($file_name,'/')!=-1
  • 你在问题​​中给出的那些例子是路径,而不是文件名,除了$file_name1,它确实是一个文件名。
  • @mob:除非您的世界只有基于 Unix 的系统,否则您会发现有许多不同的方法可以完全限定文件系统上的文件。文件的卷、路径和名称可以用多种不同的方式分隔,这就是File::Spec 存在的原因。
  • ~me 应该返回真还是假?它是一个目录,但不包含目录分隔符。
  • @ThisSuit,重读我的问题。具体来说,我想检查 file name 是否有任何目录信息。 ~me 看起来像文件名还是主目录?

标签: perl perl-module perlop


【解决方案1】:

splitdir 将返回一个列表。在标量上下文中评估,它返回列表中的项目数。如果列表中有超过 1 个项目,那么您就知道有一个目录分隔符。

use warnings;
use strict;
use File::Spec qw();

while (<DATA>) {
    chomp;
    if (File::Spec->splitdir($_) > 1) {
        print 'PASS';
    }
    else {
        print 'FAIL';
    }
    print ": $_\n";
}

__DATA__
abc.txt
dir/abc.txt
/home/me/abc.txt
~me/abc.txt

打印:

FAIL: abc.txt
PASS: dir/abc.txt
PASS: /home/me/abc.txt
PASS: ~me/abc.txt

【讨论】:

    【解决方案2】:

    示例

    ($volume,$directories,$file) =
                           File::Spec->splitpath( $path );
    

    分割路径 将路径拆分为卷、目录和文件名部分。在没有体积概念的系统上,返回 '' 表示体积。

    【讨论】:

    • 我希望有一些更简单的东西可以用作 if 语句的一部分。例如,if (!-f $file_name &amp;&amp; ...) {...add a path to the file...}
    • @MichaelGoldshteyn print "Path contains directories" if $directories;
    • 但这是多步骤的,我一直在寻找可以在 if 语句中使用的内联测试(请参阅我的第一条评论)。
    • @MichaelGoldshteyn 然后放到一个子程序中:sub dirs { my $path = shift; return (File::Spec-&gt;splitpath($path))[1]; } print "Path contains directories" if dirs("/foo/bar");
    • 或者..if ([File::Spec-&gt;splitpath( $path )]-&gt;[1]) {...}
    【解决方案3】:

    必须使用File::Spec 来获得正确可移植的结果。

    如果复杂性困扰您,那么您应该编写自己的库来包装File::Spec 函数。

    它可能看起来像这样

    文件PathLib.pm

    package PathLib;
    use strict;
    use warnings;
    
    require File::Spec;
    require Exporter;
    
    our @ISA       = qw(Exporter);
    our @EXPORT_OK = qw(has_path);
    
    sub has_path {
      my ($volume, $path, $name) = File::Spec->splitpath($_[0]);
      return ($path or $volume)
    }
    

    你可以像这样使用它。 (我目前正在使用 Windows 笔记本电脑,在我到达办公桌前无法测试 Unix 版本,但我确信这对你有用。)

    use strict;
    use warnings;
    
    use PathLib qw(has_path);
    
    my @paths = qw{
      C:\aa\bb\cc.txt
      E:ee.txt
      cc.txt
      ee\
    };
    
    for (@paths) {
      printf "%6s - %s\n", has_path($_) ? 'true' : 'false', $_;
    }
    

    输出

      true - C:\aa\bb\cc.txt
      true - E:ee.txt
     false - cc.txt
      true - ee\
    

    【讨论】:

    • 您的函数为 foo/ 返回 false。此外,您的输出是相反的(cc.txt 应该打印 no)。
    • 逻辑的反转是我在写答案的时候多次换位思考的结果,已经修复。 `ee` 的true 结果是正确的,因为路径确实包含目录信息。
    • @ThisSuitIsBlackNot:我很抱歉:我意识到我发布了我的模块的旧版本。
    【解决方案4】:

    在 Perl 中,/ 始终保留为目录分隔符(旧的 Mac OS System [6-9] 除外,它坚持使用 :)。但是,在其他操作系统中,也可以使用其他符号。例如,在 Windows 机器上,它们都指向同一个目录:

    $file = 'C:\this\that\foo.txt';
    $file = "C:/this/that/foo.txt";
    

    因此,除非您碰巧在旧 Mac 上,否则您应该始终怀疑 / 是目录说明符。而且,您还应该弄清楚该系统上当前的路径分隔符可能是什么,并寻找那个。

    我希望 File::SpecFile::Path 可能有一些函数或方法可以显示首选目录分隔符的名称,但没有记录。

    翻看各个File::Spec子模块的代码,发现实际的文件分隔符是硬编码的,功能比我想象的要复杂。没有特殊的隐藏功能。德拉特。

    不过,好消息是File::Spec 是一个标准模块,所以没有理由担心它。它甚至包含在 Perl 5.8.8 中。您不必担心它可能无法安装。

    我做了一个简单的测试:

    #! /usr/bin/env perl
    #
    use strict;
    use warnings;
    use feature qw(say);
    
    use File::Spec;
    
    while ( my $file = <DATA> ) {
        chomp $file;
        my ( $volume, $directory, $file )  = File::Spec->splitpath( $file );
        if ( $directory ) {
            say qq('$volume' '$directory' '$file' contains directory stuff);
        }
        else {
            say qq('$volume' '$directory' '$file' is pure file);
        }
    }
    
    
    __DATA__
    Foo/Bar.txt
    /Bar.txt
    Bar.txt/
    Foo_Bar.txt
    

    结果是:

    '' 'Foo/' 'Bar.txt' contains directory stuff
    '' '/' 'Bar.txt' contains directory stuff
    '' 'Bar.txt/' '' contains directory stuff
    '' '' 'Foo_Bar.txt' is pure file
    

    似乎只要出现目录分隔符,该字符串就会被视为文件。我想这是有道理的。

    这是最终比您意识到的要复杂得多的任务之一(例如确定字符串是有效的电子邮件地址还是数字)。最好的办法是使用 File::Spec-&gt;split 并确保没有设置卷或目录。

    【讨论】:

      猜你喜欢
      • 2017-04-28
      • 2011-05-04
      • 1970-01-01
      • 2014-12-23
      • 2014-05-10
      • 2023-03-27
      • 1970-01-01
      • 2010-09-10
      • 2015-10-14
      相关资源
      最近更新 更多