【问题标题】:How to find position of a word by using a counter?如何使用计数器查找单词的位置?
【发布时间】:2014-07-02 22:47:01
【问题描述】:

我目前正在研究将某些单词更改为莎士比亚单词的代码。我必须提取包含单词的句子并将它们打印到另一个文件中。我必须从每个文件的开头删除 .START 。

首先我用空格分割文件和文本,所以现在我有了单词。接下来,我通过散列迭代单词。哈希键和值来自一个制表符分隔的文件,其结构如下:OldEng/ModernEng (lc_Shakespeare_lexicon.txt)。现在,我正试图弄清楚如何找到找到的每个现代英语单词的确切位置,将其更改为莎士比亚;然后找到带有变化词的句子并将它们打印到不同的文件中。除了最后一部分之外,大部分代码都已完成。到目前为止,这是我的代码:

#!/usr/bin/perl -w
use diagnostics;
use strict;

#Declare variables
my $counter=();
my %hash=();
my $conv1=();
my $conv2=();
my $ssph=();
my @text=();
my $key=();
my $value=();
my $conversion=();
my @rmv=();
my $splits=();
my $words=();
my @word=();
my $vals=();
my $existingdir='/home/nelly/Desktop';
my @file='Sentences.txt'; 
my $eng_words=();
my $results=();
my $storage=();

#Open file to tab delimited words

open (FILE,"<", "lc_shakespeare_lexicon.txt") or die "could not open        lc_shakespeare_lexicon.txt\n";

#split words by tabs 

while (<FILE>){ 
    chomp($_);
    ($value, $key)= (split(/\t/), $_);
    $hash{$value}=$key; 
}   

#open directory to Shakespearean files

my $dir="/home/nelly/Desktop/input"; 
opendir(DIR,$dir) or die "can't opendir Shakespeare_input.tar.gz";
#Use grep to get WSJ file and store into an array

my @array= grep {/WSJ/} readdir(DIR);

#store file in a scalar
foreach my $file(@array){

    #open files inside of input

    open (DATA,"<", "/home/nelly/Desktop/input/$file") or die "could not open $file\n";
    #loop through each file

    while (<DATA>){
        @text=$_;
        chomp(@text);
    #Remove .START
    @rmv=grep(!/.START/, @text);

foreach $splits(@rmv){
    #split data into separate words
    @word=(split(/ /, $splits));
    #Loop through each word and replace with Shakespearean word that exists
    $counter=0;

foreach $words(@word){
        if (exists $hash{$words}){
            $eng_words= $hash{$words};
            $results=$counter;
            print "$counter\n";
            $counter++;

#create a new directory and store senteces with Shakespearean words in new file called "Sentences.txt"
mkdir $existingdir unless -d $existingdir; 
open my $FILE, ">>", "$existingdir/@file", or die "Can't open       $existingdir/conversion.txt'\n";
#print $FILE "@words\n";

close ($FILE);

                }           
            }
        }
    }   
}

close (FILE);
close (DIR);

【问题讨论】:

  • 你能发布一些输入数据吗?
  • 在需要之前声明变量,您会失去my 的一些好处。此外,所有这些分配(my $existingdir='/home/nelly/Desktop'; my @file='Sentences.txt'; 除外)都是无用的。
  • 您很可能会使用index pos 等 - 就像在这个similar SO question (take a look at the answers) 中一样。我不知道您是否在这里正确设置了查找%hash。尝试使用Data::DumperData::Printer 来查看它是如何填写的。
  • 文件中的句子是如何存储的?每行一个句子? Perl 代码有几个问题,请尝试在顶部添加use warnings;,然后解决警告消息。
  • 最好有use warnings 而不是-w。此外,DATA 是 Perl 的一个特殊文件句柄名称,因此您不应该将它用于您自己的文件。当前的最佳实践是对文件句柄使用词法变量 (open my $in_fh, '&lt;', $file_name or die $!)。

标签: perl


【解决方案1】:

自然语言处理很难正确处理,除非在一些琐碎的情况下,例如很难准确定义 wordsentence 的含义,以及当单引号和撇号都使用U+0027“撇号”字符' 表示时,很难区分它们。

没有任何示例数据很难编写可靠的解决方案,但下面的程序应该相当接近

请注意以下事项

  • use warnings在shebang线上比-w更可取

  • 一个程序应该包含尽可能少的 cmets,只要它是可理解的。太多的 cmets 只会让程序变得更大更难掌握,而无需添加任何新信息。标识符的选择应该使代码主要是自我记录

  • 我认为use diagnostics 是不必要的。大多数消息都是不言自明的,diagnostics 会产生大量不必要的输出

  • 因为您要打开多个文件,所以use autodie 更简洁,这将避免需要显式测试每个open 调用是否成功

  • 最好使用词法文件句柄,例如open my $fh ...,而不是使用全局句柄,例如open FH ...。一方面,词法文件句柄在超出范围时将被隐式关闭,这有助于通过使显式的close 调用不必要地整理程序

  • 我已经从程序顶部删除了所有变量声明,除了那些非空的。这种方法被认为是最佳实践,因为它有助于调试并有助于编写干净的代码

  • 程序使用lc 将原始单词小写,然后检查散列中是否存在匹配条目。如果找到翻译,如果原词以大写字母开头,则新词使用ucfirst大写

  • 我编写了一个正则表达式,它将从字符串$content 的开头获取下一个句子。但这是没有样本数据我无法做到的事情之一,并且很可能存在问题,例如以右引号或右括号结尾的句子

use strict;
use warnings;
use autodie;

my $lexicon      = 'lc_shakespeare_lexicon.txt';
my $dir          = '/home/nelly/Desktop/input';
my $existing_dir = '/home/nelly/Desktop';
my $sentences    = 'Sentences.txt';

my %lexicon = do {
  open my ($fh), '<', $lexicon;
  local $/;
  reverse(<$fh> =~ /[^\t\n\r]+/g);
};

my @files = do {
  opendir my ($dh), $dir;
  grep /WSJ/, readdir $dh;
};

for my $file (@files) {

  my $contents = do {
    open my $fh, '<', "$dir/$file";
    join '', grep { not /\A\.START/ } <$fh>;
  };

  # Change any CR or LF to a space, and reduce multiple spaces to single spaces
  $contents =~ tr/\r\n/  /;
  $contents =~ s/ {2,}/ /g;

  # Find and process each sentence
  while ( $contents =~ / \s* (.+?[.?!]) (?= \s+ [A-Z] | \s* \z ) /gx ) {
    my $sentence = $1;
    my @words    = split ' ', $sentence;
    my $changed;

    for my $word (@words) {
      my $eng_word = $lexicon{lc $word};
      $eng_word = ucfirst $eng_word if $word =~ /\A[A-Z]/;
      if ($eng_word) {
        $word = $eng_word;
        ++$changed;
      }
    }

    if ($changed) {
      mkdir $existing_dir unless -d $existing_dir;
      open my $out_fh, '>>', "$existing_dir/$sentences";
      print "@words\n";
    }
  }
}

【讨论】:

  • 谢谢,这帮助很大。我真的很感激你也指出了我的弱点!!
  • @Borodin 我很难完全理解 OP 的意图。但是,听起来他们实际上是想重建句子结构。如果是这种情况,那么正则表达式解决方案似乎更合适,而不是按空格分割。 s{(\w+)}{ ... }eg。顺便说一句,在教育方面做得很好。
猜你喜欢
  • 2016-07-30
  • 1970-01-01
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 2012-07-03
  • 1970-01-01
  • 1970-01-01
  • 2015-12-27
相关资源
最近更新 更多