如何在一系列逐步编号的文件中创建下一个文件或文件夹?

时间:2011-09-05 19:51:10

标签: perl filenames

抱歉标题不好,但这是我能做的最好的! :d

我有一个脚本,每次调用指定的函数时都会创建一个新项目。

每个项目必须存储在自己的文件夹中,并带有项目名称。但是,如果您没有指定名称,脚本只会将其命名为“new projectX”,其中X是一个渐进数字。

随着时间的推移,用户可以重命名文件夹或删除一些文件夹,因此每次脚本运行时,它都会检查可用的最小编号(未被其他文件夹使用)并创建相关文件夹。

现在我设法制作了一个我觉得可以正常工作的程序,但是我想听听你是否可以,或者由于我对该语言缺乏经验而无法发现错误。

    while ( defined( $file = readdir $projects_dir ) )
    {
        # check for files whose name start with "new project"
        if ( $file =~ m/^new project/i )
        {
            push( @files, $file );
        }
    }

    # remove letters from filenames, only the number is left
    foreach $file ( @files )
    {
        $file =~ s/[a-z]//ig;
    }

    @files = sort { $a <=> $b } @files;

    # find the smallest number available
    my $smallest_number = 0;

    foreach $file ( @files )
    {
        if ( $smallest_number != $file )
        {
            last;
        }
        $smallest_number += 1;
    }

    print "Smallest number is $smallest_number";

4 个答案:

答案 0 :(得分:3)

以下是此类问题的基本方法:

sub next_available_dir {
    my $n = 1;
    my $d;
    $n ++ while -e ($d = "new project$n");
    return $d;
}

my $project_dir = next_available_dir();
mkdir $project_dir;

如果您愿意使用与Perl string auto-increment feature完美匹配的命名模式,则可以进一步简化代码,从而无需$n。例如,newproject000

答案 1 :(得分:1)

我想我会使用类似的东西:

use strict;
use warnings;

sub new_project_dir
{
    my($base) = @_;
    opendir(my $dh, $base) || die "Failed to open directory $base for reading";
    my $file;
    my @numbers;
    while ($file = readdir $dh)
    {
        $numbers[$1] = 1 if ($file =~ m/^new project(\d+)$/)
    }
    closedir($dh) || die "Failed to close directory $base";
    my $i;
    my $max = $#numbers;
    for ($i = 0; $i < $max; $i++)
    {
        next if (defined $numbers[$i]);
        # Directory did not exist when we scanned the directory
        # But maybe it was created since then!
        my $dir = "new project$i";
        next unless mkdir "$base/$dir";
        return $dir;
    }
    # All numbers from 0..$max were in use...so try adding new numbers...
    while ($i < $max + 100)
    {
        my $dir = "new project$i";
        $i++;
        next unless mkdir "$base/$dir";
        return $dir;
    }
    # Still failed - give in...
    die "Something is amiss - all directories 0..$i in use?";
}

测试代码:

my $basedir = "base";
mkdir $basedir unless -d $basedir;

for (my $j = 0; $j < 10; $j++)
{
    my $dir = new_project_dir($basedir);
    print "Create: $dir\n";
    if ($j % 3 == 2)
    {
        my $k = int($j / 2);
        my $o = "new project$k";
        rmdir "$basedir/$o";
        print "Remove: $o\n";
    }
}

答案 2 :(得分:0)

本身没有任何错误,但这是实现单个目标的大量代码(获取目录的最小索引。

核心模块,几个子模型和少数Schwartzian变换将使代码更加灵活:

use strict;
use warnings;
use List::Util 'min';

sub num { $_[0] =~ s|\D+||g } # 'new project4' -> '4', 'new1_project4' -> '14' (!)

sub min_index {

    my ( $dir, $filter ) = @_;

    $filter = qr/./ unless defined $filter; # match all if no filter specified

    opendir my $dirHandle, $dir or die $!;

    my $lowest_index = min                 # get the smallest ...
                        map  { num($_)   }  # ... numerical value ...
                         grep {    -d     }  # ... from all directories ...
                          grep { /$filter/ }  # ... that match the filter ...
                           readdir $dirHandle; # ... from the directory contents
    $lowest_index++ while grep { $lowest_index == num( $_ ) } readdir $dirhandle;
    return $lowest_index;
}

# Ready to use!

my $index = min_index ( 'some/dir' , qr/^new project/ );

my $new_project_name = "new project $index";

答案 3 :(得分:0)

试试这个:

#!/usr/bin/env perl

use strict;
use warnings;

# get the current list of files
# see `perldoc -f glob` for details.
my @files = glob( 'some/dir/new\\ project*' );

# set to first name, in case there are none others
my $next_file = 'new project1';

# check for others
if( @files ){

  # a Schwartian transform
  @files = map  { $_->[0]                                     }  # get original
           sort { $a->[1] <=> $b->[1]                         }  # sort by second field which are numbers
           map  { [ $_, do{ ( my $n = $_ ) =~ s/\D//g; $n } ] }  # create an anonymous array with original value and the second field nothing but digits
           @files;

  # last file name is the biggest
  $next_file = $files[-1];

  # add one to it
  $next_file =~ s/(.*)(\d+)$/$1.($2+1)/e;
}

print "next file: $next_file\n";