2009-12-18 56 views
17

Tôi đã biết rằng \w trong PCRE (đặc biệt là triển khai của PHP) đôi khi có thể khớp với một số ký tự không phải ASCII tùy thuộc vào ngôn ngữ của hệ thống, nhưng còn [a-z] thì sao?[a-z] có bao giờ khớp các ký tự có dấu trọng âm trong PREG/PCRE không?

tôi sẽ không nghĩ như vậy, nhưng tôi nhận thấy những dòng này trong một trong các file lõi của Drupal (includes/theme.inc, đơn giản):

// To avoid illegal characters in the class, 
// we're removing everything disallowed. We are not using 'a-z' as that might leave 
// in certain international characters (e.g. German umlauts). 
$body_classes[] = preg_replace('![^abcdefghijklmnopqrstuvwxyz0-9-_]+!s', '', $class); 

là đúng này, hoặc đã có người chỉ đơn giản là có được [a-z] bối rối với \w?

Trả lời

13

Ngắn câu chuyện ngắn: Có thể, phụ thuộc vào hệ thống mà ứng dụng được triển khai, phụ thuộc vào cách PHP được biên dịch, chào mừng bạn đến với CF về bản địa hóa và quốc tế hóa.

Động cơ PCRE cơ bản sẽ tính toán khi xác định "a-z" có nghĩa là gì. Trong một ngôn ngữ dựa trên tiếng Tây Ban Nha, ñ sẽ bị bắt bởi a-z). Ý nghĩa ngữ nghĩa của az là "tất cả các chữ giữa a và z, và n là một lá thư riêng biệt trong tiếng Tây Ban Nha.

Tuy nhiên, cách PHP một cách mù quáng xử lý chuỗi như bộ sưu tập của byte chứ không phải là một tập hợp các điểm mã UTF Có nghĩa là bạn có một tình huống mà az MIGHT phù hợp với một ký tự có dấu trọng âm. Do nhiều hệ thống khác nhau Drupal được triển khai, nên có nghĩa là họ sẽ chọn rõ ràng về các ký tự được phép thay vì chỉ tin tưởng az để làm điều đúng.

Tôi cũng phỏng đoán rằng sự tồn tại của cụm từ thông dụng này là kết quả của báo cáo lỗi được gửi về các dấu âm tiếng Đức không được lọc.

Cập nhật vào năm 2014: mỗi JimmiTh's answer below, nó trông giống như (mặc dù một số "khó hiểu-to-phi-PCRE-core-phát triển" tài liệu) mà [a-z] sẽ chỉ phù hợp với nhân vật abcdefghijklmnopqrstuvwxyz một ngôn 99% thời gian . Điều đó nói rằng - các nhà phát triển framework có khuynh hướng cảm thấy mơ hồ về mã của họ, đặc biệt là khi mã dựa trên các hệ thống (chuỗi địa phương cụ thể) mà PHP không xử lý một cách duyên dáng như bạn muốn, và các máy chủ mà các nhà phát triển không thể kiểm soát. Mặc dù nhận xét của nhà phát triển Drupal ẩn danh là không chính xác - nhưng không phải là vấn đề "nhận được [a-z] nhầm lẫn với \w", mà thay vào đó nhà phát triển Drupal không rõ ràng/không chắc chắn về cách PCRE xử lý [a-z] và chọn hình thức cụ thể hơn abcdefghijklmnopqrstuvwxyz để đảm bảo hành vi cụ thể mà họ muốn.

+0

Điều này có thực sự đúng trong năm 2009 không? –

+0

@WalterTross Vẫn đúng như ngày hôm nay khi nó đã trở lại sau đó. Nó không bao giờ về những gì đã được/là phổ biến, đó là về những gì MIGHT xảy ra với một số cấu hình kỳ lạ, và đảm bảo mã của bạn đủ mạnh để xử lý nó. –

+1

@AlanStorm, bạn có thể cung cấp cấu hình lạ như vậy không? Tôi khá chắc chắn là không có! –

10

nhận xét trong mã của Drupal là WRONG.

Đó là NOT đúng là "international characters (e.g. German umlauts)" có thể khớp với [a-z].

Nếu, ví dụ:, Bạn có locale Đức có sẵn, bạn có thể kiểm tra xem nó như thế này:

setlocale(LC_ALL, 'de_DE'); // German locale (not needed, but you never know...) 
echo preg_match('/^[a-z]+$/', 'abc') ? "yes\n" : "no\n"; 
echo preg_match('/^[a-z]+$/', "\xE4bc") ? "yes\n" : "no\n"; // äbc in ISO-8859-1 
echo preg_match('/^[a-z]+$/', "\xC3\xA4bc") ? "yes\n" : "no\n"; // äbc in UTF-8 
echo preg_match('/^[a-z]+$/u', "\xC3\xA4bc") ? "yes\n" : "no\n"; // w/ PCRE_UTF8 

Output (sẽ không thay đổi nếu bạn thay thế de_DE với de_DE.UTF-8):

yes 
no 
no 
no 

Lớp nhân vật [abcdefghijklmnopqrstuvwxyz] giống hệt [a-z] trong cả hai mã hóa, PCRE hiểu: monobyte có nguồn gốc từ ASCII và UTF-8 (cũng có nguồn gốc từ ASCII). Trong cả hai mã hóa này, [a-z] cũng giống như [\x61-\x7A].

Mọi thứ có thể khác khi câu hỏi được hỏi trong năm 2009, nhưng trong năm 2014 không có "cấu hình lạ" có thể làm cho công cụ regex PCRE của PHP hiểu là [a-z] là một lớp gồm hơn 26 ký tự (miễn là [a-z] được viết là 5 byte trong một mã hóa có nguồn gốc ASCII, tất nhiên).

+1

Bạn đã đóng đinh +1 – HamZa

+0

Điều gì sẽ xảy ra khi tệp PHP có mã hóa của nó thay đổi? –

+0

@AlanStorm: miễn là mã hóa là ISO-8859- *, UTF-8 hoặc bất kỳ trang mã Windows nào có chứa bảng chữ cái chữ thường tiếng Anh: không có gì. Mặt khác, có vẻ như PHP có thể được biên dịch để đọc, ví dụ: mã nguồn UTF-16 (Tôi không biết điều đó). Tôi không có năng lượng để thử nó. Nếu ai đó có, họ có thể đăng những phát hiện của họ ở đây. –

7

Chỉ là sự bổ sung cho cả hai câu trả lời đã xuất sắc, nếu mâu thuẫn.

Tài liệu cho thư viện PCRE luôn nêu rằng "Phạm vi hoạt động trong chuỗi đối chiếu các giá trị ký tự". Đó là hơi mơ hồ, nhưng rất chính xác.

Nó đề cập đến đối chiếu bằng các chỉ số chỉ số trong các bảng ký tự bên trong của PCRE, có thể được thiết lập để khớp với ngôn ngữ hiện tại bằng cách sử dụng pcre_maketables. Hàm đó xây dựng các bảng theo thứ tự giá trị char (tolower(i)/toupper(i))

Nói cách khác, nó không khớp với thứ tự sắp xếp văn hóa thực tế (thông tin đối chiếu miền địa phương). Ví dụ, trong khi Đức xử lý ö giống như o trong đối chiếu từ điển, ö có giá trị làm cho nó xuất hiện bên ngoài phạm vi az trong tất cả các mã hóa ký tự chung được sử dụng cho tiếng Đức (ISO-8859-x, mã hóa unicode, v.v.) trường hợp này, PCRE sẽ căn cứ xác định xem liệu ö có nằm trong phạm vi [a-z] trên giá trị mã đó hay không, thay vì bất kỳ thứ tự sắp xếp theo miền địa phương được xác định thực tế nào.

PHP chủ yếu được sao chép PCRE's documentation nguyên văn trong their docs. Tuy nhiên, họ đã thực sự đi đến nỗi đau thay đổi tuyên bố trên cho "Phạm vi hoạt động trong chuỗi đối chiếu ASCII". Tuyên bố đó đã có trong tài liệu ít nhất kể từ năm 2004.

Mặc dù vậy, tôi không hoàn toàn chắc chắn đó là sự thật.

Vâng, không phải trong mọi trường hợp, ít nhất.

Các PHP một cuộc gọi làm để pcre_maketables ... Từ PHP source:

#if HAVE_SETLOCALE 
    if (strcmp(locale, "C")) 
     tables = pcre_maketables(); 
#endif 

Nói cách khác, nếu môi trường mà PHP được biên dịch có setlocale sự (LC_CTYPE) locale không được địa phương POSIX/C, thứ tự ký tự POSIX/C của môi trường thời gian chạy được sử dụng. Nếu không, các bảng PCRE mặc định được sử dụng - được tạo ra (bằng cách pcre_maketables) khi PCRE được biên dịch - dựa trên trình biên dịch locale:

Chức năng này xây dựng một tập hợp các bảng tính cho nhân vật giá trị ít hơn 256. Chúng có thể được chuyển tới pcre_compile() để ghi đè lên các bảng bên trong, bên trong của PCRE (được thực hiện bởi pcre_maketables() khi PCRE được biên dịch).Bạn có thể muốn làm điều này nếu bạn đang sử dụng một miền địa phương không chuẩn. Hàm này sinh ra một con trỏ tới các bảng.

Trong khi Đức sẽ không thể khác nhau cho [a-z] trong bất kỳ mã hóa ký tự thông thường, nếu chúng ta đang đối phó với EBCDIC, ví dụ, [a-z] sẽ bao gồm ± và ~. Cấp, EBCDIC là một mã hóa ký tự tôi có thể nghĩ rằng nó không đặt a-z và A-Z trong chuỗi liên tục.

Trừ khi PCRE thực hiện một số phép thuật khi sử dụng EBCDIC (và có thể), trong khi rất khó, bạn có thể bao gồm những âm sắc trong môi trường chạy hoặc môi trường chạy PHP tối ưu nhất (sử dụng riêng, rất đặc biệt, tùy chỉnh của bạn) định nghĩa địa phương), bạn có thể, trong trường hợp EBCDIC, bao gồm các ký tự không mong muốn khác. Và đối với các phạm vi khác, "đối chiếu trong chuỗi ASCII" dường như không hoàn toàn chính xác.

ETA: tôi có thể tiết kiệm một số nghiên cứu bằng cách tìm kiếm câu trả lời của riêng Philip Hazel để một mối quan tâm tương tự:

Một vấn đề khác là với các lớp nhân vật dao động. Bạn sẽ nghĩ rằng [a-k] và [x-z] được xác định rõ ràng cho các tập lệnh latin nhưng không phải như vậy.

Họ chắc chắn được xác định rõ, là tương đương với [\ x61- \ X6B] và [\ x78- \ x7a], có nghĩa là, liên quan đến trật tự mã, không văn hóa để phân loại.

+1

[pcre_maketables()] (http://vcs.pcre.org/viewvc/code/trunk/pcre_maketables.c?view=markup) chỉ xây dựng các bảng sau: bảng vỏ thấp hơn, bảng lật trường hợp, bảng lớp ký tự, bảng loại ký tự. Nó không đối phó với collations. Về EBCDIC, nếu ai đó cho tôi thấy một máy EBCDIC thực sự chạy PHP, trong đó PCRE trong PHP diễn giải '[a-z]' là '[\ x81- \ xA9]', tôi đầu hàng. –

+0

Có. Do đó "Nói cách khác, nó không khớp với thứ tự sắp xếp văn hóa thực tế (thông tin đối chiếu miền địa phương)". Toàn bộ điểm của câu trả lời * là * rằng nó không đối phó với collations. – JimmiTh

+1

Điều bitchy với strcmp (3) là nó trả về tương đương với sai, khi các chuỗi phù hợp. Vì vậy, pcre_maketables được gọi cho bất cứ điều gì, NHƯNG C miền địa phương. – Melvyn

Các vấn đề liên quan