Обновление Добавлена версия (полная программа), использующая Text::CSV
для анализа файлов
Загрузка сопоставлений в хэш (словарь), затем просмотр ваших файлов и проверка каждогополе для того, есть ли такой ключ в хэше, замените его значением, если оно есть.Запишите каждую строку во временный файл, а когда закончите, переместите его в новый файл (или перезапишите обработанный файл).Любой инструмент должен делать это, более или менее.
С Perl, протестирован с несколькими небольшими подготовленными файлами
use warnings;
use strict;
use feature 'say';
use File::Copy qw(move);
my $file = shift;
die "Usage: $0 mapping-file data-files\n" if not $file or not @ARGV;
my %map;
open my $fh, '<', $file or die "Can't open $file: $!";
while (<$fh>) {
my ($key, $val) = map { s/^\s+|\s+$//gr } split /\s*,\s*/; # see Notes
$map{$key} = $val;
}
my $outfile = "tmp.outfile.txt.$$"; # use File::Temp
foreach my $file (@ARGV) {
open my $fh_out, '>', $outfile or die "Can't open $outfile: $!";
open my $fh, '<', $file or die "Can't open $file: $!";
while (<$fh>) {
s/^\s+|\s+$//g; # remove leading/trailing whitespace
my @fields = split /\s*,\s*/;
exists($map{$_}) && ($_=$map{$_}) for @fields; # see Notes
say $fh_out join ',', @fields;
}
close $fh_out;
# Change to commented out line once thoroughly tested
#move($outfile, $file) or die "can't move $outfile to $file: $!";
move($outfile, 'new_'.$file) or die "can't move $outfile: $!";
}
Примечания.
Проверка данных по сопоставлениям написана для эффективности: мы должны смотреть на каждое поле, избежать этого нельзя, но тогда мы проверяем только поле как ключ (без регулярных выражений).Для этого необходимо убрать все начальные / конечные пробелы.Таким образом, этот код может изменить пробел в выходных файлах данных;в случае, если по какой-то причине это важно, его, конечно, можно изменить, чтобы сохранить исходные пробелы.
В комментариях упоминалось, что поле в данных может фактически отличаться при наличии дополнительных кавычек.,Затем сначала извлеките потенциальный ключ
for (@fields) {
$_ = $map{$1} if /"?([^"]*)/ and exists $map{$1};
}
Это запускает механизм регулярных выражений при каждой проверке, что влияет на эффективность.Это помогло бы очистить эти входные данные CSV от кавычек и выполнить с кодом, как указано выше, без регулярных выражений.Это может быть сделано путем чтения файлов с использованием модуля разбора CSV;см. комментарий в конце.
Для Perls ранее 5.14 замените
my ($key, $val) = map { s/^\s+|\s+$//gr } split /\s*,\s*/;
на
my ($key, $val) = map { s/^\s+|\s+$//g; $_ } split /\s*,\s*/;
, поскольку "неразрушающий"Модификатор /r
был введен только в v5.14
Если вы хотите, чтобы вся ваша операция не умерла за один плохой файл, замените or die ...
с
or do {
# print warning for whatever failed (warn "Can't open $file: $!";)
# take care of filehandles and such if/as needed
next;
};
и обязательно (возможно, зарегистрируйте и) просмотрите результат.
Это оставляет место для некоторых улучшений эффективности, но ничего существенного.
Данные с запятыми, разделяющими поля, могут (или не могут) быть действительными CSV.Поскольку этот вопрос вообще не решает эту проблему и не сообщает о проблемах, маловероятно, чтобы какие-либо свойства формата данных CSV использовались в файлах данных (разделители, встроенные в данные, защищенные кавычки).
Тем не менее, по-прежнему рекомендуется читать эти файлы с помощью модуля, поддерживающего полный CSV, например Text :: CSV .Это также облегчает задачу, заботясь о лишних пробелах и кавычках и передавая нам очищенные поля.Итак, вот что - то же, что и выше, но с использованием модуля для разбора файлов
use warnings;
use strict;
use feature 'say';
use File::Copy qw(move);
use Text::CSV;
my $file = shift;
die "Usage: $0 mapping-file data-files\n" if not $file or not @ARGV;
my $csv = Text::CSV->new ( { binary => 1, allow_whitespace => 1 } )
or die "Cannot use CSV: " . Text::CSV->error_diag ();
my %map;
open my $fh, '<', $file or die "Can't open $file: $!";
while (my $line = $csv->getline($fh)) {
$map{ $line->[0] } = $line->[1]
}
my $outfile = "tmp.outfile.txt.$$"; # use File::Temp
foreach my $file (@ARGV) {
open my $fh_out, '>', $outfile or die "Can't open $outfile: $!";
open my $fh, '<', $file or die "Can't open $file: $!";
while (my $line = $csv->getline($fh)) {
exists($map{$_}) && ($_=$map{$_}) for @$line;
say $fh_out join ',', @$line;
}
close $fh_out;
move($outfile, 'new_'.$file) or die "Can't move $outfile: $!";
}
Теперь нам не нужно беспокоиться о пробелах или общих кавычках, что немного упрощает вещи.
Несмотря на то, что трудно достоверно сравнить эти два подхода без реалистичных файлов данных, я сравнил их с (подготовленными) большими файлами данных, которые включают в себя «похожую» обработку.Код, использующий Text::CSV
для анализа, выполняется примерно одинаково или (до) на 50% быстрее.
Опция конструктора allow_whitespace делает его удалить лишние пробелыВозможно, вопреки тому, что может означать название, как я делаю от руки выше.(Также см. allow_loose_quotes
и связанные параметры.) Существует гораздо больше, см. Документы.Text::CSV
по умолчанию Text :: CSV_XS , если установлено.