2010-09-03 35 views
14

Trong các mã hóa Unicode khác nhau, ví dụ UTF-16le hoặc UTF-8, một ký tự có thể chiếm 2 hoặc 3 byte. Nhiều ứng dụng Unicode không quan tâm đến độ rộng hiển thị của các ký tự Unicode giống như chúng là tất cả các chữ cái Latinh. Ví dụ: trong văn bản -cột, phải chứa Ký tự tiếng Trung hoặc Chữ cái Latinh trong một dòng, nhưng hầu hết ứng dụng (như Eclipse, Notepad ++ và tất cả các trình soạn thảo văn bản nổi tiếng, tôi dám chắc nếu có bất kỳ ngoại lệ tốt nào) chỉ tính mỗi ký tự Trung Quốc là 1 chiều rộng như chữ Latinh. Điều này chắc chắn làm cho định dạng kết quả xấu xí và không liên kết.Làm cách nào để biết chiều rộng hiển thị ưa thích (theo cột) của các ký tự Unicode?

Ví dụ, một tab-width 8 sẽ nhận được kết quả sau xấu xí (đếm tất cả Unicode như chiều rộng 1 màn hình):

apple 10 
banana 7 
苹果  6 
猕猴桃  31 
pear 16 

Tuy nhiên, định dạng mong đợi là (Đếm từng chữ Hán như 2 chiều rộng):

apple 10 
banana 7 
苹果 6 
猕猴桃 31 
pear 16 

Tính toán không đúng trên độ rộng hiển thị của ký tự làm cho các trình chỉnh sửa này hoàn toàn vô dụng khi thực hiện sắp xếp tab và sắp xếp gói và đoạn văn bản.

Mặc dù, chiều rộng của một ký tự có thể khác nhau giữa các phông chữ khác nhau, nhưng trong mọi trường hợp phông chữ thiết bị đầu cuối có kích thước cố định, ký tự Trung Quốc luôn là chiều rộng gấp đôi. Đó là để nói, mặc dù phông chữ, mỗi ký tự Trung Quốc được ưa thích để hiển thị trong 2 chiều rộng.

Một trong những giải pháp là, tôi có thể có được chiều rộng chính xác bằng cách chuyển đổi mã hóa để GB2312, trong GB2312 mã hóa mỗi ký tự Trung Quốc mất 2 byte. tuy nhiên, một số ký tự Unicode không tồn tại trong bộ ký tự GB2312 (hoặc GBK bộ ký tự). Và nói chung, không nên tính chiều rộng màn hình từ kích thước được mã hóa theo byte.

Để chỉ cần tính toán tất cả ký tự trong Unicode trong phạm vi (\u0080 .. \uFFFF) làm 2 chiều rộng cũng không chính xác, bởi vì cũng có nhiều ký tự 1 chiều nằm rải rác trong phạm vi.

Cũng khó khi tính toán chiều rộng hiển thị của chữ cái tiếng Ả Rập và chữ cái Hàn Quốc, vì chúng xây dựng một từ/ký tự bằng số mã Unicode tùy ý. Vì vậy, chiều rộng màn hình của một điểm mã Unicode có thể không phải là một số nguyên, tôi cho rằng đó là ok, chúng có thể được nối đất thành số nguyên trong thực tế, ít nhất là tốt hơn so với số nguyên.

Vì vậy, có bất kỳ thuộc tính nào liên quan đến chiều rộng hiển thị ưa thích của một char trong tiêu chuẩn Unicode không? Hoặc bất kỳ chức năng thư viện Java nào để tính toán độ rộng màn hình?

+0

Tôi đoán vấn đề là chiều rộng phụ thuộc vào phông chữ, do đó bạn tính các ký tự (có vấn đề bạn đề cập) hoặc bạn tính toán độ dài chuỗi ký tự và sử dụng. –

Trả lời

18

Âm thanh như bạn đang tìm kiếm cái gì đó như wcwidthwcswidth, được định nghĩa trong IEEE Std 1.003,1-2001, nhưng lấy ra từ ISO C:

Chức năng wcwidth() trách nhiệm xác định số cột vị trí cần thiết cho ký tự rộng wc. Chức năng wcwidth() sẽ hoặc trở về 0 (nếu wc là một mã rộng ký tự null), hoặc trả lại số các vị trí cột để được chiếm bởi các mã rộng ký tự wc, hoặc trả lại -1 (nếu wc không tương ứng với mã có thể in rộng có thể in).

Markus Kuhn đã viết phiên bản mã nguồn mở, wcwidth.c, dựa trên Unicode 5.0. Nó bao gồm một mô tả của vấn đề, và một sự thừa nhận của việc thiếu các tiêu chuẩn trong khu vực:

Trong các thiết bị đầu ra chiều rộng cố định, các ký tự Latinh tất cả chiếm một đơn "tế bào" vị trí của chiều rộng bằng nhau, trong khi biểu ý Các ký tự CJK chiếm hai ô như vậy. Khả năng tương tác giữa thiết bị đầu cuối dòng và thiết bị đầu cuối ký tự kiểu chữ (kiểu chữ Teletype) sử dụng mã hóa UTF-8 yêu cầu thỏa thuận về ký tự nào cần nâng cao con trỏ bằng số vị trí ô. Không có các tiêu chuẩn chính thức được thiết lập tồn tại vào thời điểm hiện tại mà ký tự Unicode sẽ chiếm bao nhiêu vị trí của ô trên các thiết bị đầu cuối ký tự. Những thói quen này là nỗ lực đầu tiên xác định hành vi như vậy dựa trên các quy tắc đơn giản được áp dụng cho dữ liệu do Hiệp hội Unicode cung cấp. [...]

Nó thực hiện các quy tắc sau:

  • Các kí tự null (U + 0000) có chiều rộng cột của 0.
  • ký tự điều khiển/C1 C0 khác và DEL sẽ dẫn đến một sự trở lại giá trị -1.
  • không khoảng cách và kèm theo cách kết hợp các nhân vật (thể loại nói chung đang Mn hoặc Me trong cơ sở dữ liệu Unicode) có chiều rộng cột của 0.
  • HYPHEN SOFT (U + 00AD) có chiều rộng cột 1.
  • khác các ký tự định dạng (mã danh mục chung Cf trong cơ sở dữ liệu Unicode) và ZERO WIDTH SPACE (U + 200B) có chiều rộng cột là 0.
  • Nguyên âm trung gian Hangul Jamo và phụ âm cuối cùng (U + 1160-U + 11FF) có một cột chiều rộng của 0.
  • Ký tự giãn cách trong danh mục Độ rộng (W) hoặc Đông Á (F) Đông Á như được định nghĩa trong Báo cáo kỹ thuật Unicode # 11 có chiều rộng cột là 2.
  • Tất cả các nhân vật còn lại (bao gồm tất cả có thể in ISO 8859-1 và các nhân vật WGL4, ký tự điều khiển Unicode, vv) có chiều rộng cột 1.
+0

+1 Giải thích tuyệt vời. Mặc dù Java không có một hàm 'wcwidth()', nhưng nó rất dễ dàng để viết nó cho riêng bạn, làm theo hướng dẫn. Xem thêm câu trả lời của @ bobince để biết thêm thông tin về Độ rộng Đông Á (N/W/H/F/Na). –

4

Bạn đang nhầm lẫn giữa các điểm mã, biểu đồ và mã hóa.

Mã hóa là cách các điểm mã được chuyển đổi thành luồng octet để lưu trữ, truyền hoặc xử lý. Cả UTF-8 và UTF-16 đều là các bảng mã có độ rộng thay đổi, với các điểm mã khác nhau cần một số octet khác nhau (đối với UTF-8, từ 1 đến IIRC, 6 và UTF-16 hoặc 2 hoặc 4).

Đồ thị là "những gì chúng ta xem là một ký tự", đây là những gì được hiển thị. Một điểm mã (ví dụ:LATIN LOWER CASE A) cho một grapheme, nhưng trong các trường hợp khác có thể cần nhiều điểm mã (ví dụ: LATIN LOWER CASE A, COMBINING ACUTE và COMBINING UNDERSCORE để có chữ thường và dấu gạch dưới như được sử dụng trong Kwakwala). Trong một số trường hợp, có nhiều kết hợp các điểm mã để tạo cùng một biểu đồ (ví dụ: LATIN LOWER CASE A VỚI ACUTE và COMBINING UNDERSCORE), đây là "chuẩn hóa",

I.e. độ dài của mã hóa của một grapheme đơn sẽ phụ thuộc vào việc mã hóa và chuẩn hóa.

Chiều rộng màn hình của đồ thị sẽ phụ thuộc vào kiểu chữ, kiểu và kích thước độc lập với độ dài mã hóa.

Để biết thêm thông tin, hãy xem Wikipedia trên UnicodeUnicode's home. Ngoài ra còn có một số cuốn sách xuất sắc, có lẽ đáng chú ý nhất là "Fonts & Encodings" của Yannis Haralambous, O'Reilly.

+0

+1. Chỉ cần một nhận xét nhỏ: một mã UTF-8 mã hóa hợp lệ điểm mất đến 4 octet. –

+0

@Nemanja định nghĩa ban đầu (đối với bộ ký tự gốc 31bit gốc) hoặc định nghĩa RFC 3629/Unicode được tinh chỉnh cho mã Unicode 24 bit. Sau đó thực sự là giới hạn 4 octet vì đó là tất cả những gì cần thiết cho 24bits. – Richard

+0

Bạn nói đúng, nhưng tôi không bối rối, mặc dù tôi không sử dụng thuật ngữ chính xác. Bạn không nhận được điểm, tôi có nghĩa là phông chữ thiết bị đầu cuối có kích thước cố định ở đây và câu hỏi của tôi là về chiều rộng hiển thị ưa thích, không phải chiều rộng hiển thị chính xác. Không có nghi ngờ rằng, ví dụ, tất cả các nhân vật CJK mất 2 chiều rộng, câu hỏi của tôi là liệu Unicode cho thuộc tính như vậy, để xử lý Unicode trong cửa sổ thiết bị đầu cuối chính xác hơn. Một số ký tự (như kết hợp) được xây dựng bởi một số điểm mã, trong trường hợp này, tôi muốn biết liệu có hàm được xác định để tính chiều rộng hiển thị ưa thích từ một chuỗi hay không. –

3

Thuộc tính Unicode phản ánh khái niệm này là East_Asian_Width. Nó không thực sự đáng tin cậy như chiều rộng hình ảnh trong bối cảnh hiển thị Unicode chung, vì các ký tự không phải là người châu Á, kết hợp các ký tự, vv sẽ không xếp hàng ngay cả trong một phông chữ đơn cách. (Ví dụ của bạn chắc chắn không hiển thị xếp hàng cho tôi.)

Java không có khả năng đọc tài sản này cho nhân vật (mặc dù Android's extension không). Bạn có thể lấy nó từ ICU4J nếu bạn thực sự cần nó.

+0

Đây chính xác là những gì tôi muốn, và tập tin thuộc tính này hữu ích: http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt Nó cũng cho thấy rằng chiều rộng thay đổi được phân bố ngẫu nhiên trên tất cả. –

2

Tôi tin rằng để làm được điều này một cách chính xác, bạn cần phải xem xét rằng thành phần của tiêu chuẩn Unicode xuất bản nổi tiếng như Unicode Standard Annex #14, the Unicode Line Breaking Algorithm.

Nếu bạn được lập trình trong Perl, những gì bạn muốn biết sẽ là siêu dễ dàng, bởi vì mô-đun Unicode::LineBreak Perl thực hiện UAX # 14 bao gồm một lớp học với một phương pháp đơn giản columns cho bạn biết câu trả lời đúng cho đối số chuỗi của nó. Những điều này làm việc đặc biệt tốt trên các ngôn ngữ châu Á, nơi absolutley không có gì khác sẽ làm. Mô-đun này bao gồm hơn 6.000 bài kiểm tra đơn vị, được duy trì tích cực và tác giả của nó là chính mình là người Châu Á, vì vậy điều quan trọng đối với anh là phải nhận được những mẹo nhỏ này chính xác.

Hầu hết ruột của mô-đun là thư viện được viết bằng C. Tôi chưa xem cách gọi thư viện C thành phần của nó từ các ngôn ngữ khác thn Perl, nhưng bạn có thể xem liệu điều này có thể thực hiện được hay không.

1

Về "Hoặc bất kỳ chức năng thư viện Java nào để tính toán chiều rộng màn hình?": Nếu có tôi chưa bao giờ tìm thấy nó.

Phương pháp đơn giản nhất để tính chiều rộng của một ký tự/chuỗi là viết nó trong phông chữ GNU unicode (http://unifoundry.com/unifont.html) & đo chiều rộng ký tự. Không sạch sẽ, nhưng cho đến nay nó đã làm việc cho mọi mã hóa tôi có thể nghĩ đến.

FWIW đây là những gì tôi làm:

java.awt.font.Font MONOSPACEFONT = Font.createFont(Font.TRUETYPE_FONT, 
    new File("unifont-5.1.20080907.ttf")); 

java.awt.font.FontRenderContext FRC = new FontRenderContext(null, true, true); 

int charWidth = (int) (2.0*((java.awt.geom.Rectangle2D.Float) 
    MONOSPACEFONT.getStringBounds(stringToMeasure, FRC)).width); 

... này nên làm việc khá nhiều bất cứ nơi nào bạn triển khai JVM của bạn (nó chạy tốt trong một môi trường không đầu).

+0

Tôi đã không kiểm tra mã, nhưng điều này dường như để tính toán chiều rộng bằng pixel, nhưng không phải trong cột. (Tôi đã cập nhật tiêu đề câu hỏi để phản ánh ý định) –

+0

không - đó là cột :-) –

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