在Perl中,如何简明地检查是否定义了一个$variable并包含一个非零长度的字符串?

0vvn1miw  于 2023-01-21  发布在  Perl
关注(0)|答案(9)|浏览(150)

我目前使用以下Perl来检查变量是否已定义并包含文本。我必须首先检查defined以避免出现“uninitialized value”警告:

if (defined $name && length $name > 0) {
    # do something with $name
}

有没有更好(大概更简洁)的写法?

2sbarzqh

2sbarzqh1#

您经常会看到对已定义性的检查,因此不必处理使用undef值时的警告(在Perl 5.10中,它会告诉您违规的变量):

Use of uninitialized value $name in ...

因此,为了避开这个警告,人们想出了各种各样的代码,这些代码开始看起来像是解决方案的重要组成部分,而不是像泡泡糖和胶带一样。有时候,最好通过明确关闭你试图避免的警告来显示你正在做什么:

{
 no warnings 'uninitialized';

 if( length $name ) {
      ...
      }
 }

在其他情况下,使用某种空值代替实际数据可以解决这个问题。对于Perl 5.10's defined-or operator,给予length一个显式的空字符串(已定义,并返回零长度),而不是触发警告的变量:

use 5.010;

 if( length( $name // '' ) ) {
      ...
      }

在Perl 5.12中,这要简单一些,因为length on an undefined value also returns undefined。这可能看起来有点愚蠢,但这让我想成为的数学家感到高兴。这不会发出警告,这就是这个问题存在的原因。

use 5.012;
use warnings;

my $name;

if( length $name ) { # no warning
    ...
    }
kyvafyod

kyvafyod2#

正如mobrule所指出的,您可以使用下面的代码来节省一小笔费用:

if (defined $name && $name ne '') {
    # do something with $name
}

您可以丢弃已定义的检查,并获得更短的内容,例如:

if ($name ne '') {
    # do something with $name
}

但是,在没有定义$name的情况下,尽管逻辑流将按预期工作,但如果您使用warnings(您应该使用warnings),那么您将得到以下警告:

Use of uninitialized value in string ne

所以,如果$name可能没有定义,你真的需要首先检查定义性,以避免警告。正如希南Ünür指出的,你可以使用Scalar::MoreUtils通过empty()方法获得开箱即用的代码(检查定义性,然后检查零长度):

use Scalar::MoreUtils qw(empty);
if(not empty($name)) {
    # do something with $name 
}
aemubtdh

aemubtdh3#

首先,由于length总是返回一个非负数,

if ( length $name )

以及

if ( length $name > 0 )

是等效的。
如果可以用空字符串替换未定义的值,则可以使用Perl 5.10的//=操作符,该操作符将RHS分配给LHS,除非定义了LHS:

#!/usr/bin/perl

use feature qw( say );
use strict; use warnings;

my $name;

say 'nonempty' if length($name //= '');
say "'$name'";

请注意,没有关于未初始化变量的警告,因为如果$name未定义,它将被赋为空字符串。
但是,如果您不希望依赖于正在安装的5.10,请使用Scalar::MoreUtils提供的函数。例如,上面的代码可以写成:

#!/usr/bin/perl

use strict; use warnings;

use Scalar::MoreUtils qw( define );

my $name;

print "nonempty\n" if length($name = define $name);
print "'$name'\n";

如果不想击败$name,请使用default

afdcj2ne

afdcj2ne4#

在我不关心变量是undef还是等于''的情况下,我通常将其总结为:

$name = "" unless defined $name;
if($name ne '') {
  # do something with $name
}
axr492tv

axr492tv5#

你可以说

$name ne ""

代替

length $name > 0
uajslkp6

uajslkp66#

用简单优雅的方式做重复的事情并不总是可能的。
当您有跨多个项目复制的公共代码时,只需执行您经常执行的操作:
搜索CPAN,有人可能已经有了你的代码。对于这个问题,我找到了Scalar::MoreUtils
如果你在CPAN上找不到你喜欢的东西,那么就创建一个模块,并将代码放入子例程中:

package My::String::Util;
use strict;
use warnings;
our @ISA = qw( Exporter );
our @EXPORT = ();
our @EXPORT_OK = qw( is_nonempty);

use Carp  qw(croak);

sub is_nonempty ($) {
    croak "is_nonempty() requires an argument" 
        unless @_ == 1;

    no warnings 'uninitialized';

    return( defined $_[0] and length $_[0] != 0 );
}

1;

=head1 BOILERPLATE POD

blah blah blah

=head3 is_nonempty

Returns true if the argument is defined and has non-zero length.    

More boilerplate POD.

=cut

然后在代码中调用它:

use My::String::Util qw( is_nonempty );

if ( is_nonempty $name ) {
    # do something with $name
}

或者,如果你反对prototype,但不反对额外的括号,那么就跳过模块中的prototype,像这样调用它:is_nonempty($name).

6qfn3psc

6qfn3psc7#

优秀的库Type::Tiny提供了一个框架,可以利用它将类型检查构建到Perl代码中,我在这里展示的只是冰山一角,并且是以最简单和手动的方式使用Type::Tiny。
请务必查看Type::Tiny::Manual以了解更多信息。

use Types::Common::String qw< NonEmptyStr >;

if ( NonEmptyStr->check($name) ) {
    # Do something here.
}

NonEmptyStr->($name);  # Throw an exception if validation fails
6qftjkof

6qftjkof8#

不如

if (length ($name || '')) {
  # do something with $name
}

这并不完全等同于原始版本,因为如果$name是数值0或字符串'0',它也将返回false,但在所有其他情况下的行为相同。
在perl 5.10(或更高版本)中,适当的方法是使用defined-or操作符:

use feature ':5.10';
if (length ($name // '')) {
  # do something with $name
}

这将根据$name是否被定义而不是它是否为真来决定获取什么长度,所以0/'0'将正确处理这些情况,但是它需要一个更新的perl版本,比许多人所能获得的版本都要高。

tgabmvqs

tgabmvqs9#

if ($name )
{
    #since undef and '' both evaluate to false 
    #this should work only when string is defined and non-empty...
    #unless you're expecting someting like $name="0" which is false.
    #notice though that $name="00" is not false
}

相关问题