Có phím tắt Perl để đếm số trận đấu trong một chuỗi không?


78

Giả sử tôi có:

my $string = "one.two.three.four";

Tôi nên chơi với ngữ cảnh như thế nào để có số lần mẫu tìm thấy khớp (3)? Điều này có thể được thực hiện bằng cách sử dụng một lớp lót không?

Tôi đã thử điều này:

my ($number) = scalar($string=~/\./gi);

Tôi nghĩ rằng bằng cách đặt các dấu ngoặc đơn xung quanh $number, tôi sẽ buộc bối cảnh mảng và bằng cách sử dụng scalar, tôi sẽ nhận được số lượng. Tuy nhiên, tất cả những gì tôi nhận được là 1.

Câu trả lời:


119

Điều đó đặt bản thân regex trong ngữ cảnh vô hướng, không phải là điều bạn muốn. Thay vào đó, hãy đặt regex trong ngữ cảnh danh sách (để lấy số lượng so khớp) và đặt vào ngữ cảnh vô hướng.

 my $number = () = $string =~ /\./gi;

4
Vâng, perlsecret đề xuất "Sao Thổ" làm tên thay thế. :)
oalders

1
Ai đó có thể giải thích chút mã này cho tôi không? Tôi mới làm quen với perl và tôi vẫn chưa thực sự thoải mái với các bối cảnh.
Edward Gargan

Phần đầu tiên () = $string =~ /\./gi, làm cho toán tử đối sánh trả về kết quả của đối sánh trong ngữ cảnh danh sách. Điều này tương tự như my @results = $string =~ /\./gi;. Tiếp theo, my $numberphần là một giá trị vô hướng. Gán kết quả của ngữ cảnh danh sách cho một đại lượng vô hướng trả về độ dài của nó. Điều này giống với my $count = @some_list, trả về độ dài của mảng. Câu trả lời của tôi dưới đây là một cách khác để hình dung hành vi ở đây.
Robert P

35

Tôi nghĩ rằng cách rõ ràng nhất để mô tả điều này là tránh chuyển từ tức thời thành vô hướng. Đầu tiên gán cho một mảng, sau đó sử dụng mảng đó trong ngữ cảnh vô hướng. Về cơ bản đó là những gì = () =thành ngữ sẽ làm, nhưng không có thành ngữ (hiếm khi được sử dụng):

my $string = "one.two.three.four";
my @count = $string =~ /\./g;
print scalar @count;

15
+1 cho cách đơn giản nhất, toán tử dê rất đáng sợ.
Matteo Riva

2
Tuy nhiên, dấu ngoặc đơn xung quanh @countlà không cần thiết.
Matteo Riva

22

Ngoài ra, hãy xem Perlfaq4 :

Có một số cách, với hiệu quả khác nhau. Nếu bạn muốn đếm một ký tự đơn nhất định (X) trong một chuỗi, bạn có thể sử dụng hàm tr /// như sau:

$string = "ThisXlineXhasXsomeXx'sXinXit";
$count = ($string =~ tr/X//);
print "There are $count X characters in the string";

Điều này là tốt nếu bạn chỉ đang tìm kiếm một ký tự duy nhất. Tuy nhiên, nếu bạn đang cố gắng đếm nhiều chuỗi ký tự con trong một chuỗi lớn hơn, tr /// sẽ không hoạt động. Những gì bạn có thể làm là quấn một vòng lặp while () xung quanh khớp mẫu toàn cục. Ví dụ: hãy đếm số nguyên âm:

$string = "-9 55 48 -2 23 -76 4 14 -44";
while ($string =~ /-\d+/g) { $count++ }
print "There are $count negative numbers in the string";

Một phiên bản khác sử dụng so khớp toàn cục trong ngữ cảnh danh sách, sau đó gán kết quả cho một đại lượng vô hướng, tạo ra số lượng so khớp.

$count = () = $string =~ /-\d+/g;

9

Đoạn mã sau có phải là một đoạn mã không?

print $string =~ s/\./\./g;

6

Thử cái này:


my $string = "one.two.three.four";
my ($number) = scalar( @{[ $string=~/\./gi ]} );

Nó trả lại 3cho tôi. Bằng cách tạo tham chiếu đến một mảng, biểu thức chính quy được đánh giá trong ngữ cảnh danh sách và @{..}hủy tham chiếu tham chiếu mảng.


4
Bạn không cần bất kỳ dấu ngoặc đơn nào trong số đó.
Brad Gilbert

1
Phải nói là tôi thích phương pháp này hơn cả Goatse. Trong thực tế, tôi thích mọi thứ tốt hơn nhiều so với dê.
Wick

0

Tôi nhận thấy rằng nếu bạn có điều kiện OR trong biểu thức chính quy của mình (ví dụ /(K..K)|(V.AK)/gi:) thì mảng được tạo ra có thể có các phần tử không xác định được bao gồm trong số đếm ở cuối.

Ví dụ:

my $seq = "TSYCSKSNKRCRRKYGDDDDWWRSQYTTYCSCYTGKSGKTKGGDSCDAYYEAYGKSGKTKGGRNNR";
my $regex = '(K..K)|(V.AK)';
my $count = () = $seq =~ /$regex/gi;
print "$count\n";

Cung cấp giá trị đếm là 6.

Tôi đã tìm thấy giải pháp trong bài đăng này Làm cách nào để xóa tất cả undefs khỏi mảng?

my $seq = "TSYCSKSNKRCRRKYGDDDDWWRSQYTTYCSCYTGKSGKTKGGDSCDAYYEAYGKSGKTKGGRNNR";
my $regex = '(K..K)|(V.AK)';
my @count = $seq =~ /$regex/gi;
@count = grep defined, @count; 
my $count = scalar @count;
print "$count\n";

Sau đó đưa ra câu trả lời đúng là ba.


-1

cách khác,

my $string = "one.two.three.four";
@s = split /\./,$string;
print scalar @s - 1;

-1
my $count = 0;
my $pos = -1;
while (($pos = index($string, $match, $pos+1)) > -1) {
  $count++;
}

được kiểm tra bằng Điểm chuẩn, nó khá nhanh


Đó không phải là một mẫu phù hợp.
Jim Balter

-1

Phương pháp Friedo là: $a = () = $b =~ $c.

Nhưng có thể đơn giản hóa điều này hơn nữa để chỉ ($a) = $b =~ $c, như sau:

my ($matchcount) = $text =~ s/$findregex/ /gi;

Bạn có thể cảm ơn chỉ cần gói gọn điều này trong một hàm getMatchCount()và không phải lo lắng về việc nó phá hủy chuỗi đã truyền.

Mặt khác, bạn có thể thêm hoán đổi, có thể tính toán nhiều hơn một chút, nhưng không dẫn đến việc thay đổi chuỗi.

my ($matchcount) = $text =~ s/($findregex)/$1/gi;

Ngoại trừ việc đây là một sự thay thế, không phải một trận đấu: nó sẽ phá hủy chuỗi ban đầu. Và nó cũng giống như @Mike đã có 6 năm trước đó.
fishinear

@fishinear: Điều này rất khác so với Mike. Anh ấy có khả năng in nó, nhưng không lưu trữ nó vào một biến. Sự khác biệt là đáng kể.
HoldOffHunger

1
Nếu bạn cần không phá hủy, chỉ cần s / (regex) / $ 1 / g hoặc / (= regex) // g nếu bạn thích sống nguy hiểm.
android.weasel

@ android.weasel Ồ, này, điểm tốt! Đang cập nhật với nhận xét đó. Tôi thường bọc những thứ như thế này trong các hàm, vì vậy bản thân tôi không phải lo lắng về khả năng hủy của các args đã truyền (không chắc cái nào nhanh hơn, vì bây giờ nó đang thực hiện hoán đổi). Nhưng đó là thông tin hữu ích, thêm vào!
HoldOffHunger
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.