Как использовать переменные для подстановки в Perl? - PullRequest
3 голосов
/ 21 июля 2010

У меня есть несколько текстовых файлов, которые когда-то были таблицами в базе данных, которая теперь разбирается.Я пытаюсь собрать их, что будет легко, как только я приведу их в удобную форму.Первый файл «keys.text» представляет собой просто список меток, неправильно отформатированных.Например:

Sa 1 #
Sa 2
U 328 #*

Это всегда буква (буквы), [пробел], число (ы), [пробел] и иногда символ (ы).Текстовые файлы, соответствующие этим ключам, совпадают, за ними следует строка текста, также разделенная или разделенная пробелом.

Sa 1 # Random line of text follows.
Sa 2 This text is just as random.
U 328 #* Continuing text...

То, что я пытаюсь сделать в приведенном ниже коде, это сопоставить ключ из "keys.text" с тем же ключом в файлах .txt и поместить вкладку между ключом,и текст.Я уверен, что пропускаю что-то очень простое, но результат, который я получаю, выглядит идентично исходному .txt файлу.

Заранее спасибо за любые предложения или помощь!

#!/usr/bin/perl

use strict;
use warnings;
use diagnostics;
open(IN1, "keys.text");

my $key;

# Read each line one at a time
while ($key = <IN1>) {

# For each txt file in the current directory
foreach my $file (<*.txt>) {
  open(IN, $file) or die("Cannot open TXT file for reading: $!");
  open(OUT, ">temp.txt") or die("Cannot open output file: $!");

  # Add temp modified file into directory 
  my $newFilename = "modified\/keyed_" . $file;
  my $line;

  # Read each line one at a time
  while ($line = <IN>) {

     $line =~ s/"\$key"/"\$key" . "\/t"/;
     print(OUT "$line");

  }
  rename("temp.txt", "$newFilename");
 }   
}

РЕДАКТИРОВАТЬ: просто чтобы уточнить, результаты должны сохранить символы от клавиш, если таковые имеются.Таким образом, они будут выглядеть так:

Sa 1 #      Random line of text follows.
Sa 2        This text is just as random.
U 328 #*    Continuing text...

Ответы [ 6 ]

1 голос
/ 21 июля 2010

Регулярное выражение кажется мне довольно странным.Разве

$line =~ s/$key/$key\t/;

не будет работать лучше?

Кроме того, IIRC, <IN1> оставит новую строку в конце вашего ключа $.chomp $key чтобы избавиться от этого.

И не ставьте скобки вокруг аргументов print, особенно, когда вы пишете в дескриптор файла.Это выглядит неправильно, или нет, и отвлекает людей от реальных проблем.

0 голосов
/ 25 июля 2010

Это выглядит как идеальное место для функции map в Perl! Прочитайте весь текстовый файл в массив, затем примените функцию map по всему массиву. Единственное, что вы можете сделать, это использовать функцию quotemeta для исключения любых возможных регулярных выражений в ваших ключах.

Использование map очень эффективно. Я также считал ключи в массив, чтобы не было необходимости открывать и закрывать файл ключей в моем цикле. Это алгоритм O ^ 2, но если ваши ключи не такие большие, он не должен быть слишком плохим.

#! /usr/bin/env perl

use strict;
use vars;
use warnings;

open (KEYS, "keys.text")
    or die "Cannot open 'keys.text' for reading\n";
my @keys = <KEYS>;
close (KEYS);

foreach my $file (glob("*.txt")) {
    open (TEXT, "$file")
        or die "Cannot open '$file' for reading\n";
    my @textArray = <TEXT>;
    close (TEXT);

    foreach my $line (@keys) {
        chomp $line;
        map($_ =~ s/^$line/$line\t/, @textArray);
    }
    open (NEW_TEXT, ">$file.new") or
        die qq(Can't open file "$file" for writing\n);

    print TEXT join("\n", @textArray) . "\n";
close (TEXT);
}
0 голосов
/ 21 июля 2010

Как насчет создания двух отдельных сообщений из каждого файла. Для первого файла вы открываете ключи и создаете предварительный хеш. Для второго файла все, что вам нужно сделать, это добавить текст в хеш.

use strict;
use warnings;

my $keys_file = "path to keys.txt";
my $content_file = "path to content.txt";
my $output_file = "path to output.txt";

my %hash = ();

my $keys_regex = '^([a-zA-Z]+)\s*\(d+)\s*([^\da-zA-Z\s]+)';

open my $fh, '<', $keys_file or die "could not open $key_file";
while(<$fh>){
    my $line = $_;
    if ($line =~ /$keys_regex/){
        my $key = $1;
        my $number = $2;
        my $symbol = $3;
        $hash{$key}{'number'} = $number;
        $hash{$key}{'symbol'} = $symbol;
    }
}
close $fh;

open my $fh, '<', $content_file or die "could not open $content_file";
while(<$fh>){
    my $line = $_;
    if ($line =~ /^([a-zA-Z]+)/){
        my $key = $1;
// strip content_file line from keys/number/symbols to leave text
        line =~ s/^$key//;
        line =~ s/\s*$hash{$key}{'number'}//;
        line =~ s/\s*$hash{$key}{'symbol'}//;
        $line =~ s/^\s+//g;
        $hash{$key}{'text'} = $line;
    }
}
close $fh;

open my $fh, '>', $output_file or die "could not open $output_file";
for my $key (keys %hash){
    print $fh $key . " " . $hash{$key}{'number'} . " " . $hash{$key}{'symbol'} . "\t" . $hash{$key}{'text'} . "\n";
}
close $fh;

У меня еще не было возможности протестировать его, и решение кажется немного странным со всеми регулярными выражениями, но может дать вам представление о чем-то еще, что вы можете попробовать.

0 голосов
/ 21 июля 2010

Забавные ответы:

$line =~ s/(?<=$key)/\t/;

Где (?<=XXXX) - положительный взгляд нулевой ширины для XXXX. Это означает, что он соответствует сразу после XXXX, не будучи частью заменяемого матча.

И

$line =~ s/$key/$key . "\t"/e;

Где флаг /e в конце означает сделать один eval из того, что находится во второй половине s/// до его заполнения.

Важное примечание: Я не рекомендую ни один из них, они запутывают программу. Но они интересные. : -)

0 голосов
/ 21 июля 2010

Использование split вместо s/// упрощает задачу.В приведенном ниже коде read_keys извлекает ключи из keys.text и записывает их в хэш.

Затем для всех файлов, названных в командной строке, доступных в специальном массиве Perl @ARGV, мы проверяемкаждая строка, чтобы увидеть, начинается ли она с ключа.В противном случае мы оставляем это в покое, но в противном случае вставляем TAB между ключом и текстом.

Обратите внимание, что мы редактируем файлы на месте благодаря удобной опции Perl -i:

-i [расширение]

указывает, что файлы, обработанные конструкцией <>, должны редактироваться на месте.Это делается путем переименования входного файла, открытия выходного файла с исходным именем и выбора этого выходного файла в качестве значения по умолчанию для операторов print.Расширение, если оно указано, используется для изменения имени старого файла для создания резервной копии…

Строка split " ", $_, 3 разделяет текущую строку ровно на три поля.Это необходимо для защиты пробелов, которые могут присутствовать в текстовой части строки.

#! /usr/bin/perl -i.bak

use warnings;
use strict;

sub usage { "Usage: $0 text-file\n" }

sub read_keys {
  my $path = "keys.text";
  open my $fh, "<", $path
    or die "$0: open $path: $!";

  my %key;
  while (<$fh>) {
    my($text,$num) = split;
    ++$key{$text}{$num} if defined $text && defined $num;
  }

  wantarray ? %key : \%key;
}

die usage unless @ARGV;
my %key = read_keys;

while (<>) {
  my($text,$num,$line) = split " ", $_, 3;
  $_ = "$text $num\t$line" if defined $text &&
                              defined $num &&
                              $key{$text}{$num};
  print;
}

Пример выполнения:

$ ./add-tab input

$ diff -u input.bak input
--- input.bak   2010-07-20 20:47:38.688916978 -0500
+++ input   2010-07-20 21:00:21.119531937 -0500
@@ -1,3 +1,3 @@
-Sa 1 # Random line of text follows.
-Sa 2 This text is just as random.
-U 328 #* Continuing text...
+Sa 1   # Random line of text follows.
+Sa 2   This text is just as random.
+U 328  #* Continuing text...
0 голосов
/ 21 июля 2010

если Perl не обязателен, вы можете использовать этот awk one liner

$ cat keys.txt
Sa 1 #
Sa 2
U 328 #*

$ cat mytext.txt
Sa 1 # Random line of text follows.
Sa 2 This text is just as random.
U 328 #* Continuing text...

$ awk 'FNR==NR{ k[$1 SEP $2];next }($1 SEP $2 in k) {$2=$2"\t"}1 ' keys.txt mytext.txt
Sa 1     # Random line of text follows.
Sa 2     This text is just as random.
U 328    #* Continuing text...
...