0

我在将一个文件中的某些字符串的值替换为另一个文件中的相同字符串时遇到问题。

test1.txt

porsche  430+turbo blue
ferrari  520+supercharged red
buggati  1001+supersport black
fiat     turbo white

此 test1.txt 中的某些字符串具有自己的值,如 test2.txt 中所示

test2.txt
turbo=30
supercharged=50 
supersport=100

每当我找到相应的字符串时,我想在 test1.txt 中替换 test2.txt 中的值。

output.txt
    porsche  460 blue
    ferrari  570 red
    buggati  1101 black
    fiat     30 white

在 test1.txt 中查找 test2.txt 中的 Turbo,然后将值替换并求和。

我做了很多谷歌搜索,但我没有找到继续进行此操作的方法。谁能帮我提前谢谢

4

4 回答 4

2

amon 的解决方案既漂亮又优雅。+1(我还没有足够的声望点来对他的回答发表评论)

如果您发现不需要操作员调度表的可扩展性,这里有一个相当简单的替代方案:

(2012 年 8 月 29 日编辑:合并了 amon 的调度表,用于处理 +、-、*、/、^ 运算符)

use strict;
use warnings;
use English qw( -no_match_vars );

setup_for_testing( );

open my $source_file, '<',  'text1.txt' || die "Couldn't open source file: $OS_ERROR";
open my $key_file,    '<',  'text2.txt' || die "Couldn't open key file: $OS_ERROR";

# Clear the output file
open my $output_file, '>',  'output.txt' || die "Couldn't open output file: $OS_ERROR";

# Now open output file for append
open $output_file,    '>>', 'output.txt' || die "Couldn't open output file: $OS_ERROR";

# Create translation map from text2.txt
my %translation_map = translation_map( $key_file );

# Process text1.txt and print to output.txt
while ( my $source_line = <$source_file> ) {
    my $transformed_line = transform( $source_line, \%translation_map );
    print $output_file $transformed_line ||
        die "Couldn't print to output file: $OS_ERROR";;
}

# Tidy up
close $source_file || die "Couldn't close source file: $OS_ERROR";
close $key_file    || die "Couldn't close key file: $OS_ERROR";
close $output_file || die "Couldn't close output file: $OS_ERROR";

###################
sub setup_for_testing {
   open my $textfile1, '>',  'text1.txt' || die "Couldn't open source file: $OS_ERROR";
   open my $textfile2, '>',  'text2.txt' || die "Couldn't open key file: $OS_ERROR";

   my $source_text =<<'END_TEXT';
porsche  430-turbo blue
ferrari  520*supercharged red
buggati  1001+supersport black
fiat     turbo white
END_TEXT

   my $key_file_text =<<'END_TEXT';
turbo=30
supercharged=50
supersport=100
END_TEXT

   print $textfile1 $source_text   || die "Couldn't print to text1.txt: $OS_ERROR";
   print $textfile2 $key_file_text || die "Couldn't print to text2.txt: $OS_ERROR";

   close $textfile1 || die "Couldn't close source file: $OS_ERROR";
   close $textfile2 || die "Couldn't close key file: $OS_ERROR";

   return; # intentional void return
}

sub translation_map {
    my $key_file = shift;

    my %translation_map;
    while ( my $key_mapping = <$key_file> ) {
        chomp $key_mapping;

        # The regex /x option allows whitespace in the regular expression for readability
        my ( $key, $value ) = split / \s* = \s* /x, $key_mapping;
        $translation_map{ $key } = $value;
    }

    return %translation_map;
}

sub transform {
    my $source_line = shift @_;
    my %value_for   = %{ shift @_ };

    my $transformed_line = $source_line;

    foreach my $key ( keys %value_for ) {
        # The regex /e option causes the rights side of a substitution to be evaluated as
        # a Perl expression.
        my $value = $value_for{ $key };
        my ( $before_expression, $lvalue, $operator, $rvalue_key, $after_expression ) =
            ( $transformed_line =~ m/ \A
                                      ( .*? )
                                      ( \d+ ) ([-+*\/^]?) ( $key )
                                      ( .* )
                                      \Z
                                    /x );

        if ( $operator  ) {
            my $rvalue = $value_for{ $rvalue_key };

            # Using the dispatch table from amon's answer
            my $value_of_expression = {
              '+' => sub {$_[0] +  $_[1]},
              '-' => sub {$_[0] -  $_[1]},
              '*' => sub {$_[0] *  $_[1]},
              '/' => sub {$_[0] /  $_[1]},
              '^' => sub {$_[0] ** $_[1]},
            }->{$operator}->($lvalue, $rvalue);

            $transformed_line =
                $before_expression . $value_of_expression . $after_expression . "\n";
        } else {
            $transformed_line =~ s/$key/$value/;
        }
    }

    return $transformed_line;
}

此脚本根据您的问题规范创建测试文件 text1.txt 和 text2.txt,然后进行转换并输出到 output.txt:

> ls
stackoverflow-12169648_replace_value_of_key.pl

> perl stackoverflow-12169648_replace_value_of_key.pl 

> ls
output.txt                  text1.txt
stackoverflow-12169648_replace_value_of_key.pl  text2.txt

> more text1.txt 
porsche  430+turbo blue
ferrari  520+supercharged red
buggati  1001+supersport black
fiat     turbo white

> more text2.txt 
turbo=30
supercharged=50
supersport=100

> more output.txt 
porsche  460 blue
ferrari  570 red
buggati  1101 black
fiat     30 white

希望这是有用的。

。大卫

于 2012-08-29T04:16:13.293 回答
1

我将假设第一个文件(称为A)具有由一个或多个空白字符分隔的三列。第二列可能包含一个算术表达式,其中包含由基本(中缀)运算符分隔的十进制数字和变量。变量值固定在另一个文件中,此后称为B

准备变量很简单:

my %variables = map {chomp; split /=/, $_, 2} do {
  open my $file, "<", $filename_B or die;
  <$file>;
};

解析其他文件更加困难。假设它被打开到文件句柄$fileA中,我们遍历这些行并将每一行分成三个字段:

while (defined(my $line = <$fileA>)) {
   chomp $line;
   my ($model, $expression, $color) = split /\s+/, $line, 3;
   my $value = parseExpression($expression);
   print "\t$model $value $color\n"; # use printf to prettyprint if needed
}

然后我们将表达式值与其他数据一起打印出来,假设您要打印到 STDOUT。

我们的 subparseExpression将在操作符处拆分表达式字符串。变量名将被替换。然后这些操作严格地右关联地执行。虽然这使得解析更容易,但这并不完全自然:3*4+1评估为15. 我们使用递归是因为我更喜欢它而不是迭代能够解决多个操作:

sub parseExpression {
  my ($string) = @_;
  my ($part, $operator, $rest) = ($string =~ /(\w+)([-+*\/^]?)(.*$)/g);
  if (not $operator) {
    # $part is the whole expression
    my $value = exists $variables{$part} ? $variables{$part} : $part;
    die if $value =~ /[a-z]/i; # The variable name was not substituted
    return $value;
  } else {
    my $rval = parseExpression($rest);
    my $lval = parseExpression($part); # you don't need this
                                       # if there are no variables on the left
    my $value = {
      '+' => sub {$_[0] +  $_[1]},
      '-' => sub {$_[0] -  $_[1]},
      '*' => sub {$_[0] *  $_[1]},
      '/' => sub {$_[0] /  $_[1]},
      '^' => sub {$_[0] ** $_[1]},
    }->{$operator}->($lval, $rval);
    return $value;
  }
}

我们使用一个可爱的小调度表来为每个操作员执行适当的计算。您始终可以增强运算符正则表达式和表格以支持其他运算符。

请注意,当前的实现允许数字作为变量名。不是你可能想要的东西,但它让生活更轻松。

随机出现未定义的值可能会出现一些有趣的问题,但是这段代码应该会给你一个指向正确方向的指针。(如果您只允许第二列中的一个操作,则可以删除递归)

于 2012-08-29T01:49:09.470 回答
1

这只需分两步执行替换即可。首先找到从文件%values派生的哈希中存在的所有关键字。test2.txt然后寻找由算术运算符连接的多个十进制数并评估表达式以形成替换。

查找哈希键的正则表达式是动态构建join的,用于连接使用正则表达式交替运算符|

第二个正则表达式相当于

expression ::= digits, operator, digits, { operator, digits }

并允许术语之间有空格

use strict;
use warnings;

my %values = do {
  open my $fh, '<', 'test2.txt' or die $!;
  local $/;
  <$fh> =~ /\w+/g;
};

my $regex = join '|', keys %values;

open my $fh, '<', 'test1.txt' or die $!;

while (<$fh>) {
  s/\b($regex)\b/$values{$1}/g;
  s|([0-9]+(\s*[-+*/]\s*[0-9]+)+)|$1|eeg;
  print;
}

输出

porsche  460 blue
ferrari  570 red
buggati  1101 black
fiat     30 white
于 2012-08-29T10:59:09.547 回答
0

我根本不会用 perl 编码,但我想我可能只是提供一些整体提示......

首先,为了保持简单,您必须在某种程度上承诺使用有限的模式格式,否则正如评论中所建议的,这将成为语言解析器。所以你可能不得不说 test1.txt 中的一行可能包含也可能不包含[numbers][operator][characters]模式。

所以我可能建议将 test2.txt 解析为关联数组(哈希),这样你就有了类似的东西:

{
    "turbo"        =>  30,
    "supercharged" =>  50,
    "supersport"   => 100,
}

然后对于 test1.txt 中的每一行,您可以对以下模式执行正则表达式匹配:
\b((\d+)([+])(\w+))\b

从此模式中,您可以检查+运算符和密钥。如果你得到它们,你可以在你的散列中查找键,并在parsed number, operator, hash[key] => value

如果您只想处理 a +,那么您可以简单地将数字转换为int并添加它们。否则,要支持多个运算符模式,您将不得不显式处理它们或安全地评估字符串。

您可以在第一个主要捕获组上进行正则表达式替换

于 2012-08-29T01:33:42.890 回答