【问题标题】:perl equal strings returns 0 even if they are equalperl 相等字符串即使相等也返回 0
【发布时间】:2011-08-27 15:45:52
【问题描述】:

Perl 继续让我感到惊讶。我有一个代码,它从命令行获取输入并检查它是否在文件中。我有一个这样的文件:

ls

日期

密码

触摸

rm

首先我将这个文件读为

open(MYDATA,"filename") or die "Can not open file\n";
@commandlist = <MYDATA>;
chomp @commandlist;
close MYDATA;

参数在 $commandname 变量中。为了检查它是否正确,我打印到屏幕上。

print $commandname."\n";

效果很好。然后我写代码。

$count = @commandlist;
for($i=0;$i < $count;$i++)
{
    print $commandname;
    print $commandlist[$i];
    print "\n";
    if($commandname eq $commandlist[$i])
    {
        print "equal\n";
    }
}

并且它不打印'equal'。但它应该这样做,因为 $commandname 变量具有文件中的值 'ls'。我还打印 $commandname 和 $commandlist[$i] 的值以查看它们是否“明显”相等,然后我得到输出:

ls
lsls
lsdate
lspwd
lstouch
lsrm

在这里我看到他们得到了相同的值,但为什么 eq 运算符从不计算为零。 此外,为了完成这项任务,我尝试了各种方法,所有这些方法都变得无用,例如从数组中生成哈希并使用存在。 我为这个看似简单的问题苦苦挣扎了一天,但我就是不明白。 提前致谢

编辑: 当我改变上面的循环如下

$count = @commandlist;
for($i=0;$i < $count;$i++)
{
    print $commandlist[$i];
    print $commandname;
    print "\n";
    if($commandname eq $commandlist[$i])
    {
        print "equal\n";
    }
}

我得到了类似的输出。

ls
ls
lste
lsd
lsuch
ls

似乎由于某种原因它覆盖了某些字符。

编辑:

我的整个脚本是这样的:

#reading file code, i posted above
while(<>)
chomp($_);
$commandname = $_;
if($commandname eq "start"){
##something here
} elsif ($commandname eq "machines"){
##something here
} else {

    $count = @commandlist;
    for($i=0;$i < $count;$i++)
    {
        print $commandlist[$i];
        print $commandname;
        print "\n";
        if($commandname eq $commandlist[$i])
        {
            print "equal\n";
        }
    }

}

【问题讨论】:

  • 能不能打印一些标签里包裹的变量(比如 print "--$commandname--\n"; 或者 print "--" . $commandlist[$i] . "--\ n"; 看看变量中是否没有空格/crlf?
  • 我没有尝试过,但我知道没有任何空格,因为我得到的输出我也在上面发布。
  • “当我改变循环如下”该循环看起来与上面的相同。使用print "cmd: ($commandname)\n" 可以准确查看哪个打印属于哪个数据,以及其中包含哪些字符。
  • 字符似乎被覆盖的原因是因为您的文本文件中的行是用回车符+换行符分隔的,而不仅仅是换行符。显然,您正在使用可能配置错误的 Perl 版本的 Windows 机器上运行脚本。要修复,您需要 $commandlist[$i]=~s/\r?\n$//;在比较之前。

标签: perl string comparison


【解决方案1】:

代码中的一些更改会导致您要查找的内容,在将其放入比较之前从数组中“选择”字符串。在这里

chomp $commandlist[$i];

if($commandname eq $commandlist[$i])
{
    print "equal\n";
}

编辑:根据 perldoc chomp,当您选择列表时,您应该加上括号。所以,在你的情况下......而不是简单地说

chomp @commandlist 

让它喜欢

chomp(@commandlist)

最终编辑:我试过了,效果很好。试试看

$commandname = $ARGV[0];

open(MYDATA,"chk.txt") or die "Can not open file\n"; 
@commandlist = <MYDATA>; 
chomp(@commandlist);
close MYDATA; 

print $commandname."\n"; 

$count = @commandlist;
print $commandname; 
for($i=0;$i < $count;$i++) 
 {

 print $commandlist[$i]; 
 print "\n"; 
 if($commandname eq $commandlist[$i]) 
 {   
  print "equal\n";   
 } 
 }

【讨论】:

  • @user756790,我在我的机器上试过了,它确实有效。此外,不确定您从哪里读取 $commandname 标量(从文件或通过用户输入)......在进行比较之前尝试 chomping $commandname 。那可能会解决。
  • @user756,请参阅最终编辑。问题在于 chomping 命令行输入。希望这会有所帮助
  • 谢谢。但我仍然没有管理:) 在我的完整代码中我有一个 while 循环作为 while() 。我读了行,如果那行是“开始”,我会做一些事情,如果“机器”我会做某事……我会使用一些 if elsifs 来做这件事。所以可能我在这里遗漏了一些东西。我将添加一个编辑来显示这一点。
【解决方案2】:

覆盖表示存在 CR。这些行以CRLF 结尾,但您只需删除LFchomp。改变

while (<>) {
    chomp($_)

while (<>) {
    s/\s+\z//;

【讨论】:

    【解决方案3】:

    您可以考虑将代码重组为:

    my $path='filename';
    my $match='ls';
    

    第 1 部分 - 读取文件

    open(my $fh, '<', $path) or die "failed to open $path: $!";
    my @commandlist=<$fh>;
    chomp @commandlist;  
    # or you can combine these lines as: 
    #    chomp(my @commandlist=<$fh>);
    # because chomp operates on the array itself rather than making a copy.
    close($fh);
    

    use File::Slurp qw/ read_file /; 
    # see http://search.cpan.org/dist/File-Slurp/lib/File/Slurp.pm
    
    my @commandlist=read_file($path);  # result is pre-chomped!
    

    第 2 部分 - 检查匹配项

    foreach my $command (@commandlist) {
      print "$match equals $command\n" if $match eq $command;
    }
    

    一个重要的考虑因素是文件中的每一行必须只包含命令名称,并且不能以任何空格或制表符开头或结尾。要补偿可能的前导或尾随空格,请尝试:

    foreach my $command (@commandlist) {
      $command=~s/^\s+|\s+$//g; # strip leading or trailing whitespace
      print "$match equals $command\n" if $match eq $command;
    }
    

    最后,始终从 Perl 开发人员最好的朋友开始您的 Perl 脚本:

    use strict;
    use warnings;
    

    它将捕获大多数(如果不是全部)由草率的编程实践引起的错误。 (我们都为此受苦!)

    【讨论】:

    • 如果您的计算机上没有 File::Slurp(假设它是 Linux 机器),您可以在命令提示符处键入:'cpan File::Slurp' 来检索和加载它。对于 Windows,请使用 ActiveState Perl 包管理器。
    • 首选 open() 的三参数版本,因为它减少了奇数文件名出现意外副作用的可能性。
    • 最后,带有正则表达式的 s 运算符: s/^\s+|\s+$//g 将全局匹配行开头 (^) 或结尾 ($) 的任何空格,并且删除它。
    • 我的输入文件格式正确,我的意思是我期望的那样。一旦我得到代码工作,我会考虑使用正则表达式来使其更可靠。上述文件读取方法也与我使用的不同吗?因为从我发布的输出中我知道它没问题。可能是程序的其他部分导致了这个问题,因为我有一些 while 循环,并且 if elsif 在进入该部分之前会进行检查。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多