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


83

我目前使用以下Perl来检查变量是否已定义并包含文本。我必须首先检查defined以避免“未初始化的值”警告:

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

有没有更好的(大概更简洁)的方式来编写此代码?

Answers:


78

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

 Use of uninitialized value $name in ...

因此,为了避免这种警告,人们提出了各种各样的代码,该代码开始看起来像是解决方案的重要组成部分,而不是原来的泡泡糖和胶带。有时,最好通过显式关闭要避免的警告来显示您的操作:

 {
 no warnings 'uninitialized';

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

在其他情况下,请使用某种空值代替数据。使用Perl 5.10的define-or运算符,您可以给出length一个显式的空字符串(已定义,并返回零长度),而不是将引发警告的变量:

 use 5.010;

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

在Perl 5.12中,这有点容易,因为length在未定义的值上也会返回undefined。这可能看起来有些愚蠢,但是这让我可能想成为的数学家感到高兴。不会发出警告,这就是存在此问题的原因。

use 5.012;
use warnings;

my $name;

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

4
同样,在v5.12及更高版本中,length undef返回undef,而不是警告并返回0。在布尔上下文中,undef与0一样为false,因此,如果您的目标是v5.12或更高版本,则可以编写if (length $name) { ... }
rjbs

24

就像mobrule指出的那样,您可以使用以下方法来节省少量费用:

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

您可以放弃定义的支票,得到更短的东西,例如:

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

但是,在$name未定义的情况下,尽管逻辑流程将按预期工作,但如果您正在使用warnings(并且应该这样做),则将得到以下建议:

在字符串ne中使用未初始化的值

因此,如果有$name可能无法定义的机会,那么您确实确实需要首先检查定义是否正确,以避免发出该警告。正如SinanÜnür指出的那样,您可以使用Scalar :: MoreUtils通过以下empty()方法直接使用该代码来执行此操作:(检查定义性,然后检查零长度):

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

17

首先,由于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


为“ // =“提到+1(我怎么知道这是思南的答案:)
DVK

4
在这种情况下,我不会使用// =,因为它会改变数据的副作用。而是使用稍短的length( $name // '' )
brian d foy

@brian d'foy我认为这取决于函数执行的操作。
SinanÜnür09年

+1////=运算符可能是现有的最有用的专业运算符。
克里斯·卢茨

1
正如@rjbs在我的答案中指出的那样,使用v5.12和更高版本length现在可以返回不是数字的内容(但不是NaN;)
brian d foy 2015年

6

如果我不在乎变量是否undef等于'',通常将其总结为:

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

在Perl 5.10中,可以将其缩短为$name //= "";Sinan发布的内容。
克里斯·卢茨

即使您没有perl 5.10,您仍然可以编写$name ||= "";
RET

1
@RET:您不能使用|| 运算符,因为它用''替换了字符串'0'。您必须检查它是否已定义,而不是正确。
brian d foy

克里斯,RET:是的,我知道。我特别想建议,如果杰西卡没有与之间的分歧是关于undef"",她应该只是改变一个到另一个,并使用一个单一的测试。在一般情况下,这是行不通的,因为发布的其他解决方案要好得多,但是在这种情况下,代码会很整洁。我应该改写我的答案以使其更清楚吗?
加拉夫

1

你可以说

 $name ne ""

代替

 length $name > 0

7
这仍然会给您警告。人们首先检查定义性的原因是为了避免出现“未初始化值”警告。
brian d foy

1

并非总是可能以简单而优雅的方式来做重复的事情。

当拥有可以在许多项目中复制的通用代码时,只需执行您通常会做的事情:

搜索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
}

或者,如果你反对原型和不反对多余的括号,跳过模块中的原型,并调用它像:is_nonempty($name)


2
这不是像用锤子杀死苍蝇吗?
Zoran Simic

4
@Zoran否。这样分解代码会导致在许多不同位置复制复杂条件。这就像用针刺杀死大象。@daotoad:我认为您应该缩短答案以强调使用Scalar::MoreUtils
SinanÜnür09年

@Zoran:Scalar :: MoreUtils是一个非常轻量级的模块,没有依赖项。它的语义也是众所周知的。除非您对CPAN过敏,否则没有太多理由避免使用它。
亚当·贝莱尔

1
@Chris Lutz,是的,我不应该。但是原型是半破碎的-有简单的方法可以破坏原型的执行。例如,糟糕的和/或过时的教程继续鼓励&在调用函数时使用符号。因此,我倾向于不依靠原型来完成所有工作。我想我可以在错误消息中添加“并在子调用中使用&sigil退出,除非您确实如此”。
daotoad

1
将原型视为perl编译器的提示会更容易,因此它知道如何解析某些内容。他们不是在那里验证参数。在人们的期望方面,它们可能会被打破,但是有很多事情要做。:)
brian d foy

1

出色的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

-2

怎么样

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

这与您的原始版本并不完全相同,因为如果$name数字值为0或字符串'0',它也会返回false ,但在所有其他情况下的行为相同。

在perl 5.10(或更高版本)中,适当的方法是改为使用define-or运算符:

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

这将根据是否$name定义而不是是否正确来决定获取长度的信息,因此0 /'0'可以正确处理这些情况,但它需要的Perl版本比许多人都可用。


2
为什么要以一个破损的解决方案开始,只说它是破损的?
brian d foy

因为,正如我也提到的,5.10是“比许多人都可获得的最新版本的perl”。YMMV,但是“这是99%的解决方案,我知道您可以使用,但是有更好的解决方案,也许可以使用,也许不能使用”对我而言似乎比“这是完美的解决方案,但您可能可以使用”不要使用它,因此这是您可能可以替代的替代方案。”
Dave Sherohman 09年

1
即使使用较早的perls,您也可以使用可行的解决方案,而不是坏的解决方案。
brian d foy

-3
如果($ name)
{
    #因为undef和''都评估为false 
    #仅当定义了字符串且非空时,此方法才起作用...
    #除非您期望的东西像$ name =“ 0”,否则为false。
    #notice,尽管$ name =“ 00”不是false
}

1
不幸的是,当$ name = 0时,这将是错误的;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.