我有大量的文件可以在一些可怕的约定中对所有命名的文件进行排序 以下是一些例子:
(4)_mr__mcloughlin ____。TXT
12__sir_john_farr ____。TXT
(b)中mr__chope ____。TXT
dame_elaine_kellett - 鲍曼____。TXT
dr__blackburn ______。txt
这些名字应该是不同的人(演讲者)。另一个IT部门的某个人使用一些脚本从大量的XML文件中生成了这些文件,但是你可以看到命名是不可思议的愚蠢。
我需要为每个人排序成千上万的这些文件和多个文本文件;每个都有一些愚蠢的东西使文件名不同,无论是更多的下划线或一些随机数。他们需要按说话者排序。
使用脚本执行大部分工作会更容易,然后我可以返回并合并应该使用相同名称或其他内容的文件夹。
我有很多方法可以考虑这样做。
我打算使用Perl,但如果它值得,我可以尝试一种新语言。我不知道如何将目录中的每个文件名一次一个地读入字符串以解析为实际名称。我不完全确定如何在perl中使用正则表达式进行解析,但这可能是可谷歌的。
对于排序,我只是要使用shell命令:
`cp filename.txt /example/destination/filename.txt`
但只是因为这就是我所知道的所以它是最简单的。
我甚至没有关于我要做什么的伪代码的想法,所以如果有人知道最好的行动顺序,我的耳朵。我想我正在寻找很多帮助,我对任何建议持开放态度。许多人感谢任何可以提供帮助的人。
乙
答案 0 :(得分:5)
我希望我理解你的问题,这有点暧昧恕我直言。此代码未经测试,但应该按照我的想法执行。
use File::Copy;
sub sanatize {
local $_ = shift;
s/\b(?:dame|dr|mr|sir)\b|\d+|\(\w+\)|.txt$//g;
s/[ _]+/ /g;
s/^ | $//g;
return lc $_;
}
sub sort_files_to_dirs {
my @files = @_;
for my $filename (@files) {
my $dirname = sanatize($filename);
mkdir $dirname if not -e $dirname;
copy($filename, "$dirname/$filename");
}
}
答案 1 :(得分:2)
我有一段时间没有使用过Perl所以我要用Ruby写这个。我会评论它以建立一些伪代码。
DESTINATION = '/some/faraway/place/must/exist/and/ideally/be/empty'
# get a list of all .txt files in current directory
Dir["*.txt"].each do |filename|
# strategy:
# - chop off the extension
# - switch to all lowercase
# - get rid of everything but spaces, dashes, letters, underscores
# - then swap any run of spaces, dashes, and underscores for a single space
# - then strip whitespace off front and back
name = File.basename(filename).downcase.
gsub(/[^a-z_\s-]+/, '').gsub(/[_\s-]+/, ' ').strip
target_folder = DESTINATION + '/' + name
# make sure we dont overwrite a file
if File.exists?(target_folder) && !File.directory?(target_folder)
raise "Destination folder is a file"
# if directory doesnt exist then create it
elsif !File.exists?(target_folder)
Dir.mkdir(target_folder)
end
# now copy the file
File.copy(filename, target_folder)
end
无论如何,这就是我的想法 - 我确保所有的API调用都是正确的,但这不是经过测试的代码。这看起来像你想要完成的吗?这有助于您在Perl中编写代码吗?
答案 2 :(得分:2)
所有当前文件都在同一目录中吗?如果是这种情况,那么您可以使用'opendir'和'readdir'逐个读取所有文件。使用文件名作为键来构建哈希(删除所有'_'以及括号内的任何信息),以便得到类似的内容 -
(4)_mr__mcloughlin____.txt -> 'mr mcloughlin'
12__sir_john_farr____.txt -> 'sir john farr'
(b)mr__chope____.txt -> 'mr chope'
dame_elaine_kellett-bowman____.txt -> 'dame elaine kellett-bowman'
dr__blackburn______.txt -> 'dr blackburn'
将哈希值设置为到目前为止发生的名称的实例数。所以在这些条目之后你应该有一个看起来像这样的哈希 -
'mr mcloughlin' => 1
'sir john farr' => 1
'mr chope' => 1
'dame elaine kellett-bowman' => 1
'dr blackburn' => 1
每当您在哈希中遇到新条目时,只需使用密钥名称创建一个新目录。现在,您所要做的就是将具有更改名称的文件(使用相应的哈希值作为后缀)复制到新目录中。所以对于例如,你是偶然发现了另一个读作“mcloughlin先生”的条目然后你可以把它复制为
./mr mcloughlin/mr mcloughlin_2.txt
答案 3 :(得分:2)
我会:
定义名称中的重要内容:
dr__blackburn
与dr_blackburn
不同?dr__blackburn
与mr__blackburn
不同?提出了将名称转换为目录的规则和算法(Leon是一个非常好的开始)
读入名称并一次处理一个
如果将来需要维护和使用此脚本,我会为每个正则表达式路径创建测试(例如使用http://search.cpan.org/dist/Test-More/);当你发现一个新的皱纹,添加一个新的测试,并确保它失败,然后修复正则表达式,然后重新运行测试,以确保没有任何损坏
答案 4 :(得分:1)
您可以使用
之类的内容拆分文件名@tokens = split /_+/, $filename
对于所有这些文件名,@tokens
的最后一个条目应为".txt"
,但对于姓名拼写错误的同一个人,倒数第二个应该是相似的(或“Dr”琼斯“改为”Brian Jones“。您可能希望使用某种edit distance作为相似性指标来比较各种文件名的@tokens[-2]
;当两个条目具有相似的足够名字时,它们应该提示您作为合并的候选者。
答案 5 :(得分:1)
当你提出非常一般的问题时,只要我们有更好的规则编纂,任何语言都可以这样做。我们甚至没有细节,只有“样本”。
因此,盲目工作,看起来需要人工监控。所以这个想法是筛子。您可以反复运行并再次检查和运行的东西,并一次又一次地检查,直到您将所有内容分类为几个小的手动任务。
下面的代码使 做了很多假设 ,因为你几乎把它留给了我们处理它。其中一个是样本是所有可能姓氏的列表;如果有任何其他姓氏,请添加'em并再次运行它。
use strict;
use warnings;
use File::Copy;
use File::Find::Rule;
use File::Spec;
use Readonly;
Readonly my $SOURCE_ROOT => '/mess/they/left';
Readonly my $DEST_DIRECTORY => '/where/i/want/all/this';
my @lname_list = qw<mcloughlin farr chope kelette-bowman blackburn>;
my $lname_regex
= join( '|'
, sort { ( $b =~ /\P{Alpha}/ ) <=> ( $a =~ /\P{Alpha}/ )
|| ( length $b ) <=> ( length $a )
|| $a cmp $b
} @lname_list
)
;
my %dest_dir_for;
sub get_dest_directory {
my $case = shift;
my $dest_dir = $dest_dir_for{$case};
return $dest_dir if $dest_dir;
$dest_dir = $dest_dir_for{$case}
= File::Spec->catfile( $DEST_DIRECTORY, $case )
;
unless ( -e $dest_dir ) {
mkdir $dest_dir;
}
return $dest_dir;
}
foreach my $file_path (
File::Find::Rule->file
->name( '*.txt' )->in( $SOURCE_ROOT )
) {
my $file_name = [ File::Spec->splitpath( $file_path ) ]->[2];
$file_name =~ s/[^\p{Alpha}.-]+/_/g;
$file_name =~ s/^_//;
$file_name =~ s/_[.]/./;
my ( $case ) = $file_name =~ m/(^|_)($lname_regex)[._]/i;
next unless $case;
# as we next-ed, we're dealing with only the cases we want here.
move( $file_path
, File::Spec->catfile( get_dest_directory( lc $case )
, $file_name
)
);
}