2015-11-01 22 views
8

Tôi đang gặp phải hành vi kỳ lạ khi sử dụng thư viện locale với đầu vào unicode. Dưới đây là ví dụ làm việc tối thiểu:Ký tự Unicode không nằm trong phạm vi khi gọi locale.strxfrm

>>> x = '\U0010fefd' 
>>> ord(x) 
1113853 
>>> ord('\U0010fefd') == 0X10fefd 
True 
>>> ord(x) <= 0X10ffff 
True 
>>> import locale 
>>> locale.strxfrm(x) 
'\U0010fefd' 
>>> locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') 
'en_US.UTF-8' 
>>> locale.strxfrm(x) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: character U+110000 is not in range [U+0000; U+10ffff] 

Tôi đã thấy điều này trên Python 3.3, 3.4 và 3.5. Tôi không nhận được lỗi trên Python 2.7. Theo như tôi có thể thấy, đầu vào unicode của tôi nằm trong phạm vi unicode thích hợp, do đó, có vẻ như bằng cách nào đó nội bộ nào đó bên trong strxfrm khi sử dụng 'en_US.UTF-8' đang di chuyển đầu vào ra khỏi phạm vi.

Tôi đang chạy Mac OS X và hành vi này có thể liên quan đến http://bugs.python.org/issue23195 ... nhưng tôi đã bị ấn tượng rằng lỗi này sẽ chỉ hiển thị dưới dạng kết quả không chính xác, chứ không phải ngoại lệ được nêu ra. Tôi không thể sao chép trên máy SLES 11 của tôi, và một số khác xác nhận rằng họ không thể sao chép trên Ubuntu, CentOS hoặc Windows. Nó có thể được hướng dẫn để nghe về hệ điều hành khác trong các ý kiến.

Ai đó có thể giải thích điều gì có thể xảy ra ở đây dưới mui xe?

+0

Tôi không thể tạo lại trên Ubuntu. 'locale.strxfrm (x)' trả về ''\ x01 \ x01 \ x01 \ x01 Ւ'' trong miền địa phương' en_US.UTF-8'. – jfs

+1

bạn có thể sử dụng ['icu.Collator.createInstance (icu.Locale ('en_US')). GetSortKey' thay thế] (http://stackoverflow.com/a/32178778/4279) – jfs

+0

@JFSebastian Vâng, tôi đã sử dụng PyICU và xác nhận không có vấn đề ở đó. Tôi đã quan tâm nhiều hơn đến hành vi này trong mô-đun 'locale' stdlib và nếu đây là một số lỗi người dùng (tức là tôi đã làm điều gì đó sai) hoặc nếu có điều gì đó bất chính xảy ra hơn. – SethMMorton

Trả lời

7

Trong Python 3.x, hàm locale.strxfrm(s) sử dụng nội bộ hàm POSIX C wcsxfrm(), dựa trên cài đặt LC_COLLATE hiện tại. Các tiêu chuẩn POSIX xác định việc chuyển đổi theo cách này:

The transformation shall be such that if wcscmp() is applied to two transformed wide strings, it shall return a value greater than, equal to, or less than 0, corresponding to the result of wcscoll() applied to the same two original wide-character strings.

Định nghĩa này có thể được thực hiện theo nhiều cách, và thậm chí không đòi hỏi rằng chuỗi kết quả là có thể đọc được.

tôi đã tạo ra một ví dụ nhỏ mã C để chứng minh làm thế nào nó hoạt động:

#include <stdio.h> 
#include <wchar.h> 
#include <locale.h> 

int main() { 
    wchar_t buf[10]; 
    wchar_t *in = L"\x10fefd"; 
    int i; 

    setlocale(LC_COLLATE, "en_US.UTF-8"); 

    printf("in : "); 
    for(i=0;i<10 && in[i];i++) 
    printf(" 0x%x", in[i]); 
    printf("\n"); 

    i = wcsxfrm(buf, in, 10); 

    printf("out: "); 
    for(i=0;i<10 && buf[i];i++) 
    printf(" 0x%x", buf[i]); 
    printf("\n"); 
} 

It in chuỗi trước và sau khi chuyển đổi.

Chạy nó trên Linux (Debian Jessie) đây là kết quả:

in : 0x10fefd 
out: 0x1 0x1 0x1 0x1 0x552 

khi chạy nó trên OSX (10.11.1) kết quả là:

in : 0x10fefd 
out: 0x103 0x1 0x110000 

Bạn có thể thấy rằng đầu ra của wcsxfrm() trên OSX chứa ký tự U + 110000 không được phép trong một chuỗi Python, vì vậy đây là nguồn gốc của lỗi.

Trên Python 2.7 lỗi không được nâng lên vì triển khai locale.strxfrm() dựa trên hàm strxfrm() C.

UPDATE:

tra thêm, tôi thấy rằng định nghĩa LC_COLLATE cho en_US.UTF-8 trên OSX là một liên kết đến nét la_LN.US ASCII.

$ ls -l /usr/share/locale/en_US.UTF-8/LC_COLLATE 
lrwxr-xr-x 1 root wheel 28 Oct 1 14:24 /usr/share/locale/en_US.UTF-8/LC_COLLATE -> ../la_LN.US-ASCII/LC_COLLATE 

Tôi đã tìm thấy định nghĩa thực tế trong số sources từ Apple.Nội dung của tập tin la_LN.US-ASCII.src như sau:

order \ 
    \x00;...;\xff 

CẬP NHẬT 2:

Tôi đã tiếp tục thử nghiệm các wcsxfrm() chức năng trên OSX. Sử dụng đối chiếu la_LN.US ASCII, được đưa ra một chuỗi các nhân vật rộng C1..Cn như đầu vào, đầu ra là một chuỗi với hình thức này:

W1..Wn \x01 U1..Un 

nơi

Wx = 0x103 if Cx > 0xFF else Cx+0x3 
Ux = Cx+0x103 if Cx > 0xFF else Cx+0x3 

Sử dụng thuật toán này \x10fefd trở thành 0x103 0x1 0x110000

Tôi đã kiểm tra và mọi miền địa phương UTF-8 sử dụng đối chiếu này trên OSX, vì vậy tôi có xu hướng nói rằng hỗ trợ đối chiếu cho UTF-8 trên các hệ thống của Apple bị hỏng. Thứ tự kết quả gần như giống với thứ tự thu được so với so sánh byte bình thường, với tiền thưởng của khả năng nhận các ký tự Unicode bất hợp pháp.

+0

Huh. Vì vậy, nó có vẻ như không có nhiều tôi có thể làm để ngăn chặn 'ValueError' vì điều này đến từ thư viện C bên dưới, ngoài tầm kiểm soát của Python. – SethMMorton

+0

Tôi tự hỏi liệu đây có phải là lỗi hay không. Giả sử rằng '0x110000' là giá trị trả về hợp lệ cho' wcsxfrm() 'thì Python nên nội bộ có thể xử lý nó, đúng không? Tuy nhiên, nếu '0x110000' không hợp lệ thì tôi giả sử Python đang làm gì sẽ là" đúng ". – SethMMorton

+0

Dường như điều này xảy ra cách đây 4 năm: https://mail.python.org/pipermail/python-dev/2011-December/114759.html và http://bugs.python.org/issue13441. Từ đôi mắt của tôi nó không giống như họ tìm thấy một giải pháp cho các lỗi cho các giá trị> = '0x110000', nhưng sự đồng thuận là họ chắc chắn không muốn chúng. – SethMMorton

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