Làm cách nào để biết một biến có giá trị số trong Perl?


83

Có cách nào đơn giản trong Perl sẽ cho phép tôi xác định xem một biến nhất định có phải là số không? Một cái gì đó dọc theo dòng của:

if (is_number($x))
{ ... }

sẽ là lý tưởng. Một kỹ thuật không đưa ra cảnh báo khi công -wtắc đang được sử dụng chắc chắn được ưu tiên.

Câu trả lời:


128

Sử dụng Scalar::Util::looks_like_number()sử dụng hàm look_like_number () của API Perl C bên trong, đây có lẽ là cách hiệu quả nhất để thực hiện việc này. Lưu ý rằng các chuỗi "inf" và "infinity" được coi là số.

Thí dụ:

#!/usr/bin/perl

use warnings;
use strict;

use Scalar::Util qw(looks_like_number);

my @exprs = qw(1 5.25 0.001 1.3e8 foo bar 1dd inf infinity);

foreach my $expr (@exprs) {
    print "$expr is", looks_like_number($expr) ? '' : ' not', " a number\n";
}

Cung cấp đầu ra này:

1 is a number
5.25 is a number
0.001 is a number
1.3e8 is a number
foo is not a number
bar is not a number
1dd is not a number
inf is a number
infinity is a number

Xem thêm:


1
Và như thường lệ với tài liệu perl, việc tìm ra định nghĩa thực sự về chức năng thực hiện khá khó khăn. Theo dấu vết perldoc perlapicho chúng ta biết: Hãy kiểm tra xem nội dung của SV có giống một con số (hoặc là một con số) hay không. "Inf" và "Infinity" được coi là số (do đó sẽ không đưa ra cảnh báo không phải là số), ngay cả khi atof () của bạn không tìm kiếm chúng. Hầu như không phải là một thông số kỹ thuật có thể kiểm tra được ...
Ngày

3
Mô tả trong Scalar :: Util là tốt, look_like_number cho bạn biết liệu đầu vào của bạn có phải là thứ mà Perl sẽ coi như một số hay không, đây không nhất thiết là câu trả lời tốt nhất cho câu hỏi này. Việc đề cập đến atof là không liên quan, atof không phải là một phần của CORE :: hoặc POSIX (bạn nên xem strtod đã gộp chung atof và / là / một phần của POSIX) và giả định rằng những gì Perl nghĩ là một số là đầu vào số hợp lệ đến C chức năng rõ ràng là rất sai.
MkV

hàm rất hay :) đối với chuỗi số undef và không phải trả về 0, đối với chuỗi số trả về 1, đối với số nguyên trả về 4352 và đối với số thực trả về 8704 :) nói chung> số 0 được phát hiện. Tôi đã thử nghiệm nó trên linux.
Znik

1
Tôi thích chức năng này nói chung, nhưng hãy xem xét các số nguyên lớn. 1000000 là rất nhiều số 0 để theo dõi, xin lỗi, nhưng 1.000.000 được xem như một mảng ba phần tử, vì vậy Perl chấp nhận 1_000_000, nhưng nhìn_like_number () nói không. Làm tôi buồn.
Dave Jacoby

Lưu ý: chuỗi thập lục phân thích 0x12được không coi số bằng cách kiểm tra này.
Adam Katz

23

Kiểm tra mô-đun CPAN Regexp :: Common . Tôi nghĩ rằng nó làm chính xác những gì bạn cần và xử lý tất cả các trường hợp cạnh (ví dụ: số thực, ký hiệu khoa học, v.v.). ví dụ

use Regexp::Common;
if ($var =~ /$RE{num}{real}/) { print q{a number}; }

23

Câu hỏi ban đầu là làm thế nào để biết một biến là số, không phải nếu nó "có một giá trị số".

Có một số toán tử có các chế độ hoạt động riêng biệt cho toán hạng số và chuỗi, trong đó "số" có nghĩa là bất kỳ thứ gì ban đầu là một số hoặc đã từng được sử dụng trong ngữ cảnh số (ví dụ: trong $x = "123"; 0+$x, trước khi bổ sung, $xlà một chuỗi, sau đó nó được coi là số).

Một cách để nói là:

if ( length( do { no warnings "numeric"; $x & "" } ) ) {
    print "$x is numeric\n";
}

Nếu tính năng bitwise được bật, &chỉ tạo một toán tử số và thêm một &.toán tử chuỗi riêng biệt , bạn phải tắt nó:

if ( length( do { no if $] >= 5.022, "feature", "bitwise"; no warnings "numeric"; $x & "" } ) ) {
    print "$x is numeric\n";
}

(bitwise có sẵn trong perl 5.022 trở lên và được bật theo mặc định nếu bạn use 5.028;trở lên.)


Tuyệt vời cảm ơn bạn! Đây chính xác là những gì tôi đang tìm kiếm.
Juan A. Navarro

Nếu tôi đóng gói quy trình của bạn thành một phụ, tôi nhận được một hành vi lạ là nó phát hiện các giá trị không phải số một cách chính xác, cho đến khi tôi thử giá trị số đầu tiên, giá trị này cũng được phát hiện chính xác là đúng, nhưng sau đó, mọi thứ khác từ đó trở đi cũng đúng. Tuy nhiên, khi tôi đặt một đánh giá xung quanh phần độ dài (...), nó hoạt động tốt mọi lúc. Bất kỳ ý tưởng những gì tôi đã thiếu? sub numeric { $obj = shift; no warnings "numeric"; return eval('length($obj & "")'); }
yogibimbi

@yogibimbi: bạn đang sử dụng lại cùng một biến $ obj mỗi lần; thử my $obj = shift;. Tại sao đánh giá?
ysth

oops, xấu của tôi, tôi đã sử dụng my $obj = shift, tất nhiên, chỉ là không chuyển nó chính xác từ mã của tôi đến nhận xét, tôi đã chỉnh sửa nó một chút. Tuy nhiên, sub numeric { my $obj = shift; no warnings "numeric"; return length($obj & ""); }tạo ra cùng một lỗi. Tất nhiên, có một biến toàn cục bí mật sẽ giải thích hành vi, nó chính xác là những gì tôi mong đợi trong trường hợp đó, nhưng thật không may, nó không đơn giản như vậy. Ngoài ra, điều đó sẽ bị bắt bởi strict& warnings. Tôi đã thử đánh giá trong một nỗ lực khá tuyệt vọng để loại bỏ lỗi và nó đã hoạt động. Không có lý luận sâu hơn, chỉ cần thử và sai.
yogibimbi

Check it out: sub numeric { my $obj = shift; no warnings "numeric"; return length($obj & ""); } print numeric("w") . "\n"; #=>0, print numeric("x") . "\n"; #=>0, print numeric("1") . "\n"; #=>0, print numeric(3) . "\n"; #=>1, print numeric("w") . "\n"; #=>1. Nếu bạn đặt eval ('') xung quanh độ dài, bản in cuối cùng sẽ cho kết quả là 0, giống như vậy. Đi tìm hình.
yogibimbi

9

Một câu trả lời đơn giản (và có thể đơn giản) cho câu hỏi là nội dung của $xsố như sau:

if ($x  eq  $x+0) { .... }

Nó thực hiện một so sánh văn bản của bản gốc $xvới $xgiá trị được chuyển đổi thành giá trị số.


1
Điều đó sẽ đưa ra cảnh báo nếu bạn sử dụng "-w" hoặc "sử dụng cảnh báo;".
Công viên Derek

1
Các cảnh báo có thể được gỡ bỏ $x eq (($x+0)."")tuy nhiên, một vấn đề tồi tệ hơn là trong hàm này, "1.0" không phải là số
Eponymous

1
nó là đủ thử nghiệm $ x + 0 ne ''. khi bạn nhắn tin 0001, thì số chính xác sẽ được kiểm tra là không phải số. tương tự khi bạn kiểm tra giá trị văn bản '.05'.
Znik

8

Thông thường xác thực số được thực hiện với các biểu thức chính quy. Mã này sẽ xác định xem thứ gì đó có phải là số hay không cũng như kiểm tra các biến không xác định để không đưa ra cảnh báo:

sub is_integer {
   defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
}

sub is_float {
   defined $_[0] && $_[0] =~ /^[+-]?\d+(\.\d+)?$/;
}

Đây là một số tài liệu đọc bạn nên xem.


2
Tôi thực sự nghĩ rằng điều này hơi lạc đề, đặc biệt là khi người hỏi nói / đơn giản /. Nhiều trường hợp, kể cả ký hiệu khoa học, hầu như không đơn giản. Trừ khi sử dụng điều này cho một mô-đun, tôi sẽ không lo lắng về các chi tiết như vậy. Đôi khi sự đơn giản là tốt nhất. Đừng cho siro sô cô la vào bò để làm sô cô la sữa!
osirisgothra

'.7' có lẽ là một trong những trường hợp đơn giản nhất vẫn bị bỏ sót ... tốt hơn hãy thử /^[+-]?\d*\.?\d+$/ cho float. Biến thể của tôi, cũng được xem xét ký hiệu khoa học: /^[+-]?\d*\.?\d+(?:(?:e|E)\d+)?$/
Aconcagua

Phần \d*\.?\d+giới thiệu rủi ro ReDoS . Tôi khuyên bạn nên /^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?$/hay /^[+-]?(?!\.(?!\d)|$)\d*(?:\.\d*)?(?:(?<=[\d.])e[+-]?\d+)?$/iđể bao gồm ký hiệu khoa học ( giải thích và ví dụ ) để thay thế. Điều này sử dụng một lookahead phủ định được nhân đôi để ngăn các chuỗi giống như ..e0chuyển dưới dạng số. Nó cũng sử dụng một cái nhìn tích cực để đảm bảo emột số sau.
Adam Katz

3

Không hoàn hảo, nhưng bạn có thể sử dụng regex:

sub isnumber 
{
    shift =~ /^-?\d+\.?\d*$/;
}

Cùng một vấn đề như câu trả lời của andrewrk: bỏ sót nhiều trường hợp thậm chí đơn giản, ví dụ như' .7'
Aconcagua


2

Có thể tìm thấy một regex mạnh mẽ hơn một chút trong Regexp :: Common .

Có vẻ như bạn muốn biết nếu Perl nghĩ một biến là số. Đây là một chức năng bẫy cảnh báo đó:

sub is_number{
  my $n = shift;
  my $ret = 1;
  $SIG{"__WARN__"} = sub {$ret = 0};
  eval { my $x = $n + 1 };
  return $ret
}

Một tùy chọn khác là tắt cảnh báo cục bộ:

{
  no warnings "numeric"; # Ignore "isn't numeric" warning
  ...                    # Use a variable that might not be numeric
}

Lưu ý rằng các biến không phải số sẽ được chuyển đổi âm thầm thành 0, đây có thể là điều bạn muốn.


2

rexep không hoàn hảo ... đây là:

use Try::Tiny;

sub is_numeric {
  my ($x) = @_;
  my $numeric = 1;
  try {
    use warnings FATAL => qw/numeric/;
    0 + $x;
  }
  catch {
    $numeric = 0;
  };
  return $numeric;
}

1

Thử đi:

If (($x !~ /\D/) && ($x ne "")) { ... }

1

Tôi thấy điều này thú vị mặc dù

if ( $value + 0 eq $value) {
    # A number
    push @args, $value;
} else {
    # A string
    push @args, "'$value'";
}

bạn cần giải thích một cách tốt hơn, bạn đang nói rằng bạn thấy nó thú vị nhưng nó có trả lời cho op không? Cố gắng giải thích tại sao câu trả lời của bạn là giải pháp cho câu hỏi hỏi
Kumar Saurabh

Ví dụ: giá trị $ của tôi là 1, $ value + 0 vẫn bằng 1. Bằng cách so sánh với $ value 1 bằng 1. Nếu giá trị $ là một chuỗi nói "swadhi" thì $ value + 0 trở thành giá trị ascii của chuỗi "swadhi" + 0 = một số khác.
Swadhikar

0

Cá nhân tôi nghĩ rằng cách để đi là dựa vào bối cảnh bên trong của Perl để đưa ra giải pháp chống đạn. Một regexp tốt có thể khớp với tất cả các giá trị số hợp lệ và không có giá trị nào không phải là số (hoặc ngược lại), nhưng vì có một cách sử dụng cùng một logic mà trình thông dịch đang sử dụng nên sẽ an toàn hơn khi dựa vào đó trực tiếp.

Khi tôi có xu hướng chạy các tập lệnh của mình -w, tôi phải kết hợp ý tưởng so sánh kết quả của "giá trị cộng với 0" với giá trị ban đầu với no warningscách tiếp cận dựa trên @ysth:

do { 
    no warnings "numeric";
    if ($x + 0 ne $x) { return "not numeric"; } else { return "numeric"; }
}


-1

if (đã định nghĩa $ x && $ x! ~ m / \ D /) {} hoặc $ x = 0 nếu! $ x; nếu ($ x! ~ m / \ D /) {}

Đây là một sự thay đổi nhỏ trong câu trả lời của Veekay nhưng hãy để tôi giải thích lý do của tôi cho sự thay đổi.

Thực hiện regex trên một giá trị không xác định sẽ gây ra lỗi và sẽ khiến mã thoát trong nhiều nếu không phải hầu hết các môi trường. Kiểm tra xem giá trị có được xác định hay không hoặc đặt trường hợp mặc định như tôi đã làm trong ví dụ thay thế trước khi chạy biểu thức, ít nhất sẽ lưu nhật ký lỗi của bạn.

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.