perl 使用Tab / Space分隔输出

lb3vh1jj  于 2023-06-23  发布在  Perl
关注(0)|答案(3)|浏览(193)

我正在处理三个文本文件。第一个是主输入(输入1),单词和单词类型(名词,动词等)由制表符分隔。
输入1

John    N
goes    V
to      P
school  N
.       S
Mary    N
comes   V
from    P
home    N
.       S

第二个和第三个输入文本文件如下所示:
输入2

John
Mary

输入3

to
from

我的目标是将第二个和第三个文本文件与主输入进行比较和匹配,并得到如下输出:
预期输出:

John    N   N
goes    V
to      P   P
school  N
.       S
Mary    N   N
comes   V
from    P   P
home    N
.       S

所有三列都应该用制表符或空格分隔。但是,我得到了这样的输出:

John N  
N
goes    
V
to P    
P
school  
N
.   
S
Mary N  
N
comes   
V
from P  
P
home    
N
.   
S

我相信这是发生在我把第一个文本文件的输入到一个数组中并打印值的时候。请建议一种方法来获得所需的输出。
我使用的程序代码如下:

#!/usr/bin/perl

use warnings;
use strict;

my @file = ('Input 1.txt');

open my $word_fh, '<', 'Input 2.txt' or die $!;
open my $word2_fh, '<', 'Input 3.txt' or die $!;

my %words_to_match = map {chomp $_; $_ => 0} <$word_fh>;
my %words_to_match2 = map {chomp $_; $_ => 0} <$word2_fh>;

close $word_fh;
close $word2_fh;

check($_) for @file;

sub check {
    my $file = shift;

open my $fh, '<', $file or die $!;

while (<$fh>){
    chomp;
    my @words_in_line = split;

    for my $word (@words_in_line){
        $word =~ s/[(\.,;:!)]//g;
        $word .= '  N' if exists $words_to_match{$word};
        $word .= '  P' if exists $words_to_match2{$word};
        
        print "$word\n";
    }
    print "\n";
}

同样,我们的目标是让输出的三列都由制表符或空格分隔。

xzabzqsa

xzabzqsa1#

如果您先读取所有的引用文件并从中构建数据结构,然后再读取主输入文件并对其进行转换,这会使事情变得容易得多
你使用了两个散列,%words_to_match%words_to_match2,并存储了值为零的每个元素。这是对信息的浪费,这里最好的方法是构建一个 * 单个 * 散列,将每个引用文件中的单词与它们的词性联系起来。Input 2.txt中的词是名词,所以它们得到N;而Input 3.txt中的词是介词,所以它们得到P
然后,您只需检查是否存在与Input 1.txt中的每个单词匹配的哈希元素,如果存在,则在打印记录之前追加其值
下面的程序创建了一个哈希%pos,如下所示,它将两个引用文件中的每个单词与其词性关联起来

( from => "P", John => "N", Mary => "N", to => "P" )

在最后一个输入循环中,我使用了一个替换s///,将所有尾随的空格(包括换行符)替换为三个空格和词性。制表符对于布局表格来说并不是很有用,首先是因为没有人能同意制表符应该停在哪里,其次是因为单个制表符并不总是与列对齐。根据前面数据中的字符数,有时可能需要两个或更多字符
我希望它是清楚的

use strict;
use warnings 'all';
use autodie;

my %words;

my %files = (
    'input 2.txt' => 'N',   
    'input 3.txt' => 'P',   
);

while ( my ( $file, $pos ) = each %files ) {
    open my $fh, '<', $file;

    while ( <$fh> ) {
        s/\s+\z//;
        $words{$_} = $pos;
    }
}

{
    open my $fh, '<','input 1.txt';

    while ( <$fh> ) {
        next unless /\S/;
        my ($word) = split;
        my $pos = $words{$word};
        s/\s+\z/   $pos\n/ if $pos;
    }
    continue {
        print;
    }
}

输出

John    N   N
goes    V
to      P   P
school  N
.       S
Mary    N   N
comes   V
from    P   P
home    N
.       S
atmip9wb

atmip9wb2#

您正在输出一个不必要的换行符,并且您正在错误地构造新的输出行。不需要在散列中搜索“type”列。这将产生所需的输出。

use warnings;
use strict;

my @file = ('Input 1.txt');

open my $word_fh,  '<', 'Input 2.txt' or die $!;
open my $word2_fh, '<', 'Input 3.txt' or die $!;

my %words_to_match  = map { chomp $_; $_ => 0 } <$word_fh>;
my %words_to_match2 = map { chomp $_; $_ => 0 } <$word2_fh>;

close $word_fh;
close $word2_fh;

check($_) for @file;

sub check {
    my $file = shift;
    open my $fh, '<', $file or die $!;
    while (<$fh>) {
        chomp;
        my ($word, $type) = split;
        my $line = $_;
        $line .= '  N' if exists $words_to_match{$word};
        $line .= '  P' if exists $words_to_match2{$word};
        print "$line\n";
    }
}
kse8i1jr

kse8i1jr3#

问题是这样的:

my @words_in_line = split;

for my $word (@words_in_line){
    ...
}

你要做的是查看行中的 * 第一个单词 *,看看它是否匹配任何%words_to_match变量,如果匹配,则将NP附加到 * 整行 *。
现在你看到的是一行中的每个单词,而不仅仅是第一个单词。然后将NP附加到单词本身,而不是整行。
下面是正确的伪代码:

# get the first word in the line
# if it matches `%words_to_match` then append the `  N` to the entire line
# if it matches `%words_to_match2` then append the `  P` to the entire line
# print the line

我从答案的第一段中得到了这个伪代码,并将其分解为几部分。
在Perl中,它看起来像这样:

my ($first_word) = split;

$_ .= '  N' if exists $words_to_match{$first_word};
$_ .= '  P' if exists $words_to_match2{$first_word};

print "$_\n";

相关问题