2009-08-28 65 views
11

Tôi có chuỗi "re\x{0301}sume\x{0301}" (in như thế này: re & # x0301; sume & # x0301;) và tôi muốn đảo ngược nó thành "e\x{0301}muse\x{0301}r" (e & # x0301; muse & # x0301; r). Tôi không thể sử dụng số reverse của Perl vì nó xử lý các ký tự kết hợp như "\x{0301}" làm các ký tự riêng biệt, vì vậy, tôi sẽ nhận được "\x{0301}emus\x{0301}er" (& # x0301; emus & # x0301; er). Làm thế nào tôi có thể đảo ngược chuỗi, nhưng vẫn tôn trọng các ký tự kết hợp?Làm cách nào để đảo ngược chuỗi có chứa các ký tự kết hợp trong Perl?

Trả lời

8

Câu trả lời tốt nhất là sử dụng Unicode::GCString, as Sinan points out


tôi sửa đổi dụ Chas của một chút:

  • Đặt mã hóa trên STDOUT để tránh cảnh báo "ký tự in rộng";
  • Sử dụng một sự khẳng định lookahead tích cực (và không có chế độ giữ separator) trong split (không hoạt động sau 5.10, rõ ràng, vì vậy tôi loại bỏ nó)

Đó là cơ bản điều tương tự với một số điều chỉnh.

use strict; 
use warnings; 

binmode STDOUT, ":utf8"; 

my $original = "re\x{0301}sume\x{0301}"; 
my $wrong = reverse $original; 
my $right = join '', reverse split /(\X)/, $original; 

print <<HERE; 
original: [$original] 
    wrong: [$wrong] 
    right: [$right] 
HERE 
+0

Wow. Tôi thích perl, nhưng biểu hiện phân chia đó khá là huyền diệu. Suy nghĩ đầu tiên của tôi là "sức mạnh vũ phu": tạo ra một chức năng để làm những gì mà sự phân chia làm - trả về một danh sách các chuỗi, mỗi mục trong đó đại diện cho một ký tự logic. Tuy nhiên bạn nhận được danh sách đó (gọi nó là @x), phần nối ('', đảo ngược (@x)) rõ ràng là sau, may mắn thay. – Roboprog

+2

Magical? Làm thế nào? Nó chỉ là một regex không có tác dụng phụ và nó chỉ làm chính xác những gì bạn nhìn thấy. Nếu bạn nghĩ đó là phép thuật, bạn chưa từng thấy nghệ thuật đen thực sự của Perl. Bạn có thể gọi nó là thông minh (mặc dù tôi sẽ không), nhưng nó không phải là huyền diệu. Nó có lẽ chỉ là một cái gì đó bạn chưa từng sử dụng. –

+0

Tôi đã thử chạy ví dụ này bằng Perl v5.12.4 và nó không hoạt động. Sử dụng/(\ X)/thay vào đó. Không quan tâm, câu trả lời này có hoạt động trong các phiên bản trước của Perl không, hoặc chúng ta có bỏ lỡ điều hiển nhiên không? – Flimm

12

Bạn có thể sử dụng \X special escape (phù hợp với một ký tự kết hợp không và tất cả các nhân vật kết hợp sau đây) với split để tạo ra một danh sách các graphemes (với chuỗi rỗng giữa chúng), đảo ngược danh sách các graphemes, sau đó join chúng trở lại với nhau:

#!/usr/bin/perl 

use strict; 
use warnings; 

my $original = "re\x{0301}sume\x{0301}"; 
my $wrong = reverse $original; 
my $right = join '', reverse split /(\X)/, $original; 
print "original: $original\n", 
     "wrong: $wrong\n", 
     "right: $right\n"; 
+1

Đối với những nhầm lẫn (như tôi là lúc đầu) về lý do tại sao có những chuỗi rỗng giữa graphemes, đó là vì 'split' được đảo ngược: nó sử dụng các dữ liệu đó là muốn tách. Chuỗi rỗng là "giữa" hai đồ thị là gì. Nó chỉ bằng cách bao gồm dấu phân cách trong kết quả mà bạn nhận được các đồ thị được trộn lẫn với kết quả "thực" - một chuỗi các chuỗi rỗng. Phương thức thay thế (và hơi nhanh hơn) tránh được đó là sử dụng 'm // g' để nắm bắt đồ thị thay vì:' join '', đảo ngược $ original = ~/(\ X)/g' –

+2

Để làm rõ nhận xét của Michael , khi bạn sử dụng dấu ngoặc đơn bộ nhớ trong một regex bạn cung cấp để phân chia, bạn kích hoạt "chế độ giữ chân phân cách". Bạn lấy lại thứ nằm giữa các phần bạn đang tách ra. Tuy nhiên, bạn không cần phải làm điều đó. Mẫu (? = \ X) thực hiện điều tương tự mà không cần thêm bit. Không phải là chuỗi rỗng thực sự quan trọng đối với các chuỗi nhỏ. –

+0

Bạn có quyền chỉ ra "chế độ lưu giữ dấu phân tách", cảm ơn bạn, điều đó hữu ích. Tuy nhiên, (? = \ X) không tương đương. Đối với giấy tờ chứng minh, hãy xem xét hai ví dụ này: chia/(a) /, "abc" là không tương đương với chia/(= a) /, "abc" và chia/(b + c) /, "abbcd" không tương đương với split/(? = b + c) /, "abbcd" – Flimm

0

Một số câu trả lời khác chứa các thành phần không hoạt động tốt. Đây là một ví dụ làm việc được kiểm tra trên Perl 5.12 và 5.14. Việc không chỉ định binmode sẽ làm cho đầu ra tạo ra các thông báo lỗi. Sử dụng xác nhận lookahead tích cực (và không có chế độ lưu giữ dấu tách) trong phần chia sẽ làm cho đầu ra không chính xác trên Macbook của tôi.

#!/usr/bin/perl 

use strict; 
use warnings; 
use feature 'unicode_strings'; 

binmode STDOUT, ":utf8"; 

my $original = "re\x{0301}sume\x{0301}"; 
my $wrong = reverse $original; 
my $right = join '', reverse split /(\X)/, $original; 
print "original: $original\n", 
     "wrong: $wrong\n", 
     "right: $right\n"; 
2

Bạn có thể sử dụng Unicode::GCString:

Unicode :: GCString đối xử với Unicode chuỗi như một chuỗi các cụm grapheme mở rộng được xác định bởi Unicode chuẩn Phụ lụC# 29 [UAX # 29].

#!/usr/bin/env perl 

use utf8; 
use strict; 
use warnings; 
use feature 'say'; 
use open qw(:std :utf8); 

use Unicode::GCString; 

my $x = "re\x{0301}sume\x{0301}"; 
my $y = Unicode::GCString->new($x); 
my $wrong = reverse $x; 
my $correct = join '', reverse @{ $y->as_arrayref }; 

say "$x -> $wrong"; 
say "$y -> $correct"; 

Output:

résumé -> ́emuśer 
résumé -> émusér
Các vấn đề liên quan