Trong Perl, làm cách nào để tôi có thể kiểm tra một cách chính xác xem một biến $ đã được xác định và có chứa một chuỗi độ dài khác 0 hay không?


83

Tôi hiện đang sử dụng Perl sau để kiểm tra xem một biến có được xác định và chứa văn bản hay không. Tôi phải kiểm tra definedtrước để tránh cảnh báo 'giá trị chưa được khởi tạo':

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

Có cách nào tốt hơn (có lẽ ngắn gọn hơn) để viết điều này không?

Câu trả lời:


78

Bạn thường thấy việc kiểm tra tính xác định để không phải đối mặt với cảnh báo sử dụng giá trị undef (và trong Perl 5.10, nó cho bạn biết biến vi phạm):

 Use of uninitialized value $name in ...

Vì vậy, để giải quyết cảnh báo này, mọi người nghĩ ra đủ loại mã và mã đó bắt đầu giống như một phần quan trọng của giải pháp hơn là kẹo cao su bong bóng và băng keo. Đôi khi, tốt hơn là bạn nên thể hiện những gì bạn đang làm bằng cách tắt một cách rõ ràng cảnh báo rằng bạn đang cố tránh:

 {
 no warnings 'uninitialized';

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

Trong các trường hợp khác, hãy sử dụng một số loại giá trị null thay vì dữ liệu. Với toán tử hoặc được định nghĩa của Perl 5.10 , bạn có thể cung cấp lengthmột chuỗi rỗng rõ ràng (được xác định và trả lại độ dài bằng 0) thay vì biến sẽ kích hoạt cảnh báo:

 use 5.010;

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

Trong Perl 5.12, nó dễ dàng hơn một chút vì lengthtrên một giá trị không xác định cũng trả về không xác định . Điều đó có vẻ hơi ngớ ngẩn, nhưng điều đó làm hài lòng nhà toán học mà tôi có thể muốn trở thành. Điều đó không đưa ra cảnh báo, đó là lý do câu hỏi này tồn tại.

use 5.012;
use warnings;

my $name;

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

4
Ngoài ra, trong v5.12 trở lên, length undeftrả về undef, thay vì cảnh báo và trả về 0. Trong ngữ cảnh boolean, undef chỉ sai bằng 0, vì vậy nếu bạn đang nhắm mục tiêu v5.12 trở lên, bạn chỉ có thể viếtif (length $name) { ... }
rjbs

24

Như mobrule chỉ ra, bạn có thể sử dụng những thứ sau để tiết kiệm một khoản tiền nhỏ:

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

Bạn có thể bỏ kiểm tra đã xác định và nhận được một cái gì đó thậm chí còn ngắn hơn, ví dụ:

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

Nhưng trong trường hợp $namekhông được xác định, mặc dù luồng logic sẽ hoạt động như dự định, nhưng nếu bạn đang sử dụng warnings(và bạn nên làm như vậy), thì bạn sẽ nhận được lời khuyên sau:

Sử dụng giá trị chưa được khởi tạo trong chuỗi ne

Vì vậy, nếu có một cơ hội $namecó thể không được xác định, bạn thực sự cần phải kiểm tra tính xác định trước tiên và quan trọng nhất để tránh cảnh báo đó. Như Sinan Ünür chỉ ra, bạn có thể sử dụng Scalar :: MoreUtils để lấy mã thực hiện chính xác điều đó (kiểm tra độ xác định, sau đó kiểm tra độ dài bằng 0) thông qua empty()phương pháp:

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

17

Đầu tiên, vì lengthluôn trả về một số không âm,

if ( length $name )

if ( length $name > 0 )

là tương đương.

Nếu bạn đồng ý với việc thay thế một giá trị không xác định bằng một chuỗi trống, bạn có thể sử dụng //=toán tử Perl 5.10 để gán RHS cho LHS trừ khi LHS được xác định:

#!/usr/bin/perl

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

my $name;

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

Lưu ý rằng không có cảnh báo về một biến chưa được khởi tạo như $nameđược gán cho chuỗi trống nếu nó chưa được xác định.

Tuy nhiên, nếu bạn không muốn phụ thuộc vào việc cài đặt 5.10, hãy sử dụng các chức năng được cung cấp bởi Scalar :: MoreUtils . Ví dụ, ở trên có thể được viết là:

#!/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";

Nếu bạn không muốn làm tắc nghẽn $name, hãy sử dụng default.


1 cho "// =" đề cập đến (Sao tôi biết rằng muốn có câu trả lời Sinan :)
DVK

4
Tôi sẽ không sử dụng // = trong trường hợp này vì nó thay đổi dữ liệu như một tác dụng phụ. Thay vào đó, hãy sử dụng ngắn hơn một chút length( $name // '' ).
brian d foy 26/09/09

@brian d'foy Tôi nghĩ nó phụ thuộc vào những gì đang được thực hiện trong hàm.
Sinan Ünür

+1 Toán tử ////=có thể là những toán tử chuyên biệt hữu ích nhất đang tồn tại.
Chris Lutz

1
Như @rjbs chỉ ra trong câu trả lời của tôi, với v5.12 và sau đó lengthbây giờ có thể trở lại một cái gì đó không phải là một số (nhưng không NaN;)
brian d Foy

6

Trong trường hợp tôi không quan tâm biến đó undefbằng hay bằng '', tôi thường tóm tắt nó như sau:

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

Trong Perl 5.10, điều này có thể được rút ngắn thành $name //= "";chính xác những gì Sinan đã đăng.
Chris Lutz

Và ngay cả khi bạn không có perl 5.10, bạn vẫn có thể viết$name ||= "";
RET

1
@RET: bạn không thể sử dụng || ở đây vì nó thay thế chuỗi '0' bằng ''. Bạn phải kiểm tra xem nó được xác định, không đúng sự thật.
brian d foy 29/09/09

Chris, RET: Đúng, tôi biết. Tôi đặc biệt cố gắng gợi ý rằng nếu Jessica không quan tâm đến sự khác biệt giữa undef"", cô ấy chỉ nên đổi cái này sang cái kia và sử dụng một phép thử duy nhất. Điều này sẽ không hoạt động trong trường hợp chung, mà các giải pháp khác được đăng theo cách tốt hơn, nhưng trong trường hợp cụ thể này dẫn đến mã gọn gàng. Tôi có nên diễn đạt lại câu trả lời của mình để làm rõ hơn điều này không?
Gaurav 29/09/09

1

Bạn có thể nói

 $name ne ""

thay vì

 length $name > 0

7
Điều này vẫn sẽ cung cấp cho bạn một cảnh báo. Lý do mọi người kiểm tra độ xác định trước là để tránh cảnh báo 'giá trị chưa được khởi tạo'.
brian d foy 26/09/09

1

Không phải lúc nào bạn cũng có thể làm những việc lặp đi lặp lại một cách đơn giản và thanh lịch.

Chỉ cần làm những gì bạn luôn làm khi bạn có mã chung được sao chép qua nhiều dự án:

Tìm kiếm CPAN, ai đó có thể đã có mã cho bạn. Đối với vấn đề này, tôi đã tìm thấy Scalar :: MoreUtils .

Nếu bạn không tìm thấy thứ mình thích trên CPAN, hãy tạo một mô-đun và đặt mã vào một chương trình con:

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

Sau đó, trong mã của bạn, hãy gọi nó là:

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

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

Hoặc nếu bạn phản đối nguyên mẫu và không phản đối việc Parens thêm, bỏ qua nguyên mẫu trong các mô-đun, và gọi nó là như sau: is_nonempty($name).


2
Điều này không giống như sử dụng một cái búa để giết một con ruồi?
Zoran Simic

4
@Zoran Không. Mã bao thanh toán giống như thế này có một điều kiện phức tạp được sao chép ở nhiều nơi khác nhau. Điều đó giống như sử dụng kim châm để giết một con voi. @daotoad: Tôi nghĩ bạn nên rút ngắn câu trả lời của mình để nhấn mạnh việc sử dụng Scalar::MoreUtils.
Sinan Ünür

@Zoran: Scalar :: MoreUtils là một mô-đun rất nhẹ không có phụ thuộc. Ngữ nghĩa của nó cũng được nhiều người biết đến. Trừ khi bạn bị dị ứng với CPAN, không có nhiều lý do để tránh sử dụng nó.
Adam Bellaire 26/09/09

1
@Chris Lutz, vâng, tôi không nên. Tuy nhiên, nguyên mẫu là một nửa bị hỏng - có nhiều cách dễ dàng để phá vỡ việc thực thi nguyên mẫu. Ví dụ, các hướng dẫn cũ kỹ và / hoặc lỗi thời tiếp tục khuyến khích việc sử dụng &dấu hiệu khi gọi các hàm. Vì vậy, tôi có xu hướng không dựa vào nguyên mẫu để thực hiện tất cả các công việc. Tôi cho rằng tôi có thể thêm "và bỏ sử dụng & sigil trên các cuộc gọi phụ trừ khi bạn thực sự có ý đó" vào thông báo lỗi.
daotoad 28/09/09

1
Sẽ dễ dàng hơn khi nghĩ về các nguyên mẫu như là gợi ý cho trình biên dịch perl để nó biết cách phân tích cú pháp một cái gì đó. Chúng không ở đó để xác thực các đối số. Chúng có thể bị phá vỡ về sự mong đợi của mọi người, nhưng rất nhiều thứ là như vậy. :)
brian d foy

1

Thư viện tuyệt vời Type :: Tiny cung cấp một khuôn khổ để xây dựng tính năng kiểm tra kiểu vào mã Perl của bạn. Những gì tôi hiển thị ở đây chỉ là phần nổi mỏng nhất của tảng băng trôi và đang sử dụng Type :: Tiny theo cách thủ công và đơn giản nhất.

Hãy nhớ xem Type :: Tiny :: Manual để biết thêm thông tin.

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

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

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

-2

Làm thế nào về

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

Điều này không hoàn toàn tương đương với phiên bản gốc của bạn, vì nó cũng sẽ trả về false nếu $namelà giá trị số 0 hoặc chuỗi '0', nhưng sẽ hoạt động giống nhau trong tất cả các trường hợp khác.

Trong phần 5.10 (hoặc mới hơn), cách tiếp cận thích hợp sẽ là sử dụng toán tử-hoặc được xác định thay thế:

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

Điều này sẽ quyết định việc lấy độ dài dựa trên việc có $nameđược xác định hay không, hơn là liệu nó có đúng hay không, vì vậy 0 / '0'sẽ xử lý những trường hợp đó một cách chính xác, nhưng nó yêu cầu phiên bản perl mới hơn nhiều người có.


2
Tại sao dẫn đầu với một giải pháp bị hỏng chỉ để nói rằng nó đã bị hỏng?
brian d foy 26/09/09

Bởi vì, như tôi cũng đã đề cập, 5.10 là "một phiên bản perl gần đây hơn nhiều người có sẵn." YMMV. không sử dụng nó, vì vậy đây là một giải pháp thay thế mà bạn có thể có được như một phương án dự phòng. "
Dave Sherohman

1
Ngay cả với các đặc quyền trước đó, bạn có thể có một giải pháp hoạt động thay vì một giải pháp bị hỏng.
brian d foy 28/09/09

-3
nếu ($ name)
{
    #since undef và '' đều đánh giá là false 
    #this sẽ chỉ hoạt động khi chuỗi được xác định và không trống ...
    #unless bạn đang mong đợi một số thông tin như $ name = "0" là sai.
    #notice mặc dù $ name = "00" không sai
}

1
Thật không may, điều này sẽ sai khi $ name = 0;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.