TCHAR có còn phù hợp không?


87

Tôi mới làm quen với lập trình Windows và sau khi đọc cuốn sách Petzold, tôi tự hỏi:

Có phải thực hành tốt là sử dụng TCHARkiểu và _T()hàm để khai báo chuỗi hay tôi chỉ nên sử dụng chuỗi wchar_tL""trong mã mới?

Tôi sẽ chỉ nhắm mục tiêu Windows 2000 trở lên và mã của tôi sẽ là i18n ngay từ đầu.

Câu trả lời:


15

Tôi sẽ vẫn sử dụng cú pháp TCHAR nếu tôi đang thực hiện một dự án mới hôm nay. Không có nhiều sự khác biệt thực tế giữa việc sử dụng nó và cú pháp WCHAR, và tôi thích mã rõ ràng về kiểu ký tự. Vì hầu hết các hàm API và các đối tượng trợ giúp sử dụng / sử dụng các kiểu TCHAR (ví dụ: CString), nên việc sử dụng nó là hợp lý. Thêm vào đó, nó mang lại cho bạn sự linh hoạt nếu bạn quyết định sử dụng mã trong ứng dụng ASCII vào một thời điểm nào đó hoặc nếu Windows từng phát triển thành Unicode32, v.v.

Nếu bạn quyết định đi theo con đường WCHAR, tôi sẽ nói rõ về nó. Đó là, sử dụng CStringW thay vì CString và truyền macro khi chuyển đổi sang TCHAR (ví dụ: CW2CT).

Đó là ý kiến ​​của tôi, dù sao.


Thật vậy, đó là những gì sẽ vẫn hoạt động khi mã hóa ký tự cuối cùng được thay đổi '' một lần nữa ''.
Medinoc

11
Bạn thích mã rõ ràng về kiểu ký tự và do đó sử dụng kiểu đôi khi là kiểu này và đôi khi là kiểu kia? Rất thuyết phục.
Deduplicator

4
−1 đối với sự không nhất quán được ghi nhận bởi @Deduplicator và đối với lời khuyên hoàn trả tiêu cực để sử dụng macro có thể là bất kỳ điều gì (và thường sẽ không được kiểm tra cho nhiều hơn một giá trị cụ thể).
Chúc mừng và hth. - Alf

90

Câu trả lời ngắn gọn: KHÔNG .

Giống như tất cả những cái khác đã viết, nhiều lập trình viên vẫn sử dụng TCHAR và các hàm tương ứng. Theo ý kiến ​​khiêm tốn của tôi , toàn bộ khái niệm là một ý tưởng tồi . Xử lý chuỗi UTF-16 khác rất nhiều so với xử lý chuỗi ASCII / MBCS đơn giản. Nếu bạn sử dụng các thuật toán / chức năng giống nhau với cả hai (đây là ý tưởng TCHAR dựa trên!), Bạn sẽ có hiệu suất rất tệ trên phiên bản UTF-16 nếu bạn đang thực hiện nhiều hơn một chút so với nối chuỗi đơn giản (như phân tích cú pháp, v.v.). Lý do chính là Người thay thế .

Với ngoại lệ duy nhất khi bạn thực sự phải biên dịch ứng dụng của mình cho một hệ thống không hỗ trợ Unicode, tôi thấy không có lý do gì để sử dụng hành lý này trước đây trong một ứng dụng mới.


6
Sự thật thú vị: UTF-16 không phải lúc nào cũng có trên nền tảng NT. Các điểm mã thay thế được giới thiệu với Unicode 2.0, vào năm 1996, cũng là năm NT 4 được phát hành. Cho đến khi, IIRC, (bao gồm) Windows 2000, tất cả các phiên bản NT đều sử dụng UCS-2, một tập hợp con của UTF-16 giả định rằng mỗi ký tự có thể biểu diễn được bằng một điểm mã (tức là không có đại diện).
0xC0000022L

3
btw, trong khi tôi đồng ý rằng TCHARkhông nên được sử dụng nữa, tôi không đồng ý rằng đây là một ý tưởng tồi. Tôi cũng nghĩ rằng nếu bạn chọn rõ ràng thay vì sử dụng, TCHARbạn nên rõ ràng ở mọi nơi . Tức là cũng không sử dụng các hàm có TCHAR/ _TCHAR(chẳng hạn như _tmain) trong khai báo của chúng. Nói một cách đơn giản: hãy kiên định. +1, vẫn còn.
0xC0000022L

3
Đó là một ý tưởng hay khi nó được giới thiệu, nhưng nó sẽ không liên quan trong mã mới.
Adrian McCarthy

4
Bạn đã trình bày sai, những gì TCHARđược giới thiệu ban đầu là: Để dễ dàng phát triển mã cho các phiên bản Windows dựa trên Win 9x và Windows NT. Vào thời điểm đó, việc triển khai UTF-16 của Windows NT là UCS-2 và các thuật toán để phân tích / thao tác chuỗi là giống hệt nhau. Không có người đại diện. Và ngay cả với các đại diện, các thuật toán cho DBCS (mã hóa MBCS duy nhất được hỗ trợ cho Windows) và UTF-16 đều giống nhau: Trong một trong hai cách mã hóa, một điểm mã bao gồm một hoặc hai đơn vị mã.
IInspectable

Giả sử tôi muốn sử dụng FormatMessage () để chuyển đổi một giá trị từ WSAGetLastError () thành một thứ có thể in được. Tài liệu cho WSAGetLastError () cho biết nó sử dụng LPTSTR làm con trỏ tới bộ đệm. Tôi thực sự không có nhiều lựa chọn ngoài việc sử dụng TCHAR, phải không?
Edward Falk

80

Tôi phải đồng ý với Sascha. Tiền đề cơ bản của TCHAR/ _T()/ v.v. là bạn có thể viết một ứng dụng dựa trên "ANSI" và sau đó cung cấp cho nó hỗ trợ Unicode một cách kỳ diệu bằng cách xác định một macro. Nhưng điều này dựa trên một số giả định xấu:

Rằng bạn tích cực xây dựng cả phiên bản MBCS và Unicode cho phần mềm của mình

Nếu không, bạn sẽ trượt lên và sử dụng char*dây thông thường ở nhiều nơi.

Việc bạn không sử dụng dấu gạch chéo ngược không phải ASCII sẽ thoát ra trong _T ("...") ký tự

Trừ khi mã hóa "ANSI" của bạn là ISO-8859-1, kết quả char*và các wchar_t*ký tự sẽ không đại diện cho các ký tự giống nhau.

Chuỗi UTF-16 đó được sử dụng giống như chuỗi "ANSI"

Không phải vậy. Unicode giới thiệu một số khái niệm không tồn tại trong hầu hết các bảng mã ký tự cũ. Người đẻ thuê. Kết hợp các ký tự. Bình thường hóa. Quy tắc viết hoa có điều kiện và ngôn ngữ nhạy cảm.

Và có lẽ quan trọng nhất, thực tế là UTF-16 hiếm khi được lưu trên đĩa hoặc được gửi qua Internet: UTF-8 có xu hướng được ưu tiên hơn để biểu diễn bên ngoài.

Rằng ứng dụng của bạn không sử dụng Internet

(Bây giờ, đây có thể là một giả định hợp lệ cho phần mềm của bạn , nhưng ...)

Web chạy trên UTF-8rất nhiều mã hóa hiếm hơn . Các TCHARkhái niệm chỉ công nhận hai: "ANSI" (mà không thể được UTF-8 ) và "Unicode" (UTF-16). Nó có thể hữu ích để làm cho các lệnh gọi API Windows của bạn nhận biết được Unicode, nhưng nó vô dụng khi làm cho các ứng dụng web và e-mail của bạn nhận biết được Unicode.

Mà bạn không sử dụng thư viện không phải của Microsoft

Không ai khác sử dụng TCHAR. Poco sử dụng std::stringvà UTF-8. SQLite có các phiên bản UTF-8 và UTF-16 của API của nó, nhưng không TCHAR. TCHARthậm chí không có trong thư viện chuẩn, vì vậy không std::tcouttrừ khi bạn muốn tự mình xác định nó.

Những gì tôi đề xuất thay vì TCHAR

Quên rằng mã hóa "ANSI" tồn tại, ngoại trừ khi bạn cần đọc tệp không hợp lệ UTF-8. Quên về TCHARquá. Luôn gọi phiên bản "W" của các hàm API Windows. #define _UNICODEchỉ để đảm bảo rằng bạn không vô tình gọi một hàm "A".

Luôn sử dụng mã hóa UTF cho chuỗi: UTF-8 cho charchuỗi và UTF-16 (trên Windows) hoặc UTF-32 (trên hệ thống giống Unix) cho wchar_tchuỗi. typedef UTF16UTF32các loại ký tự để tránh sự khác biệt về nền tảng.


6
Cuộc gọi năm 2012: vẫn còn ứng dụng được duy trì mà không có #define _UNICODEngay cả bây giờ. Kết thúc truyền :)
0xC0000022L

12
@ 0xC0000022L câu hỏi là về mã mới . Khi bạn duy trì mã cũ, rõ ràng bạn phải làm việc với môi trường mã được viết. Nếu bạn đang duy trì một ứng dụng COBOL, thì không quan trọng COBOL có phải là một ngôn ngữ tốt hay không, bạn đang mắc kẹt với nó. Và nếu bạn đang duy trì một ứng dụng dựa vào TCHAR thì không quan trọng đó có phải là một quyết định tốt hay không, bạn đang mắc kẹt với nó.
jalf

2
Thật vậy, TCHAR là không hữu ích nếu trong COBOL)
Pavel Radzivilovsky

1
_UNICODEkiểm soát cách ánh xạ văn bản chung được giải quyết trong CRT. Nếu bạn không muốn gọi phiên bản ANSI của API Windows, bạn cần phải xác định UNICODE.
IInspectable

18

Nếu bạn đang tự hỏi liệu nó có còn được sử dụng trong thực tế hay không, thì có - nó vẫn được sử dụng khá nhiều. Sẽ không ai nhìn mã của bạn buồn cười nếu nó sử dụng TCHAR và _T (""). Dự án tôi đang thực hiện hiện đang chuyển đổi từ ANSI sang unicode - và chúng tôi đang đi theo lộ trình di động (TCHAR).

Tuy nhiên...

Phiếu bầu của tôi sẽ là quên tất cả các macro di động ANSI / UNICODE (TCHAR, _T ("") và tất cả các lệnh gọi _tXXXXXX, v.v.) và chỉ giả sử unicode ở mọi nơi. Tôi thực sự không thấy điểm di động nếu bạn sẽ không bao giờ cần phiên bản ANSI. Tôi sẽ sử dụng trực tiếp tất cả các hàm và kiểu ký tự rộng. Viết trước tất cả các ký tự chuỗi bằng L.


3
Bạn có thể viết một số mã mà bạn muốn sử dụng ở một nơi khác, nơi bạn cần phiên bản ANSI, hoặc (như Nick đã nói) Windows có thể chuyển sang DCHAR hoặc bất cứ thứ gì, vì vậy tôi vẫn nghĩ rằng tốt hơn hết bạn nên sử dụng TCHAR thay vì CUỘC CHIẾN.
arke

Tôi nghi ngờ rằng Windows sẽ chuyển sang UTF-32.
dan04 22/10/12

7
-1 cho khuyến nghị UTF-16. Điều này không chỉ tạo ra mã không di động (tập trung vào cửa sổ), điều này không thể chấp nhận được đối với các thư viện - mặc dù có thể được sử dụng cho các trường hợp đơn giản nhất như mã giao diện người dùng - nó không hiệu quả ngay cả trên chính Windows. utf8everywhere.org
Pavel Radzivilovsky

11

Bài viết Giới thiệu về Lập trình Windows trên MSDN cho biết

Các ứng dụng mới phải luôn gọi các phiên bản Unicode (của API).

Các TEXTTCHAR macro là ít hữu ích ngày hôm nay, bởi vì tất cả các ứng dụng nên sử dụng Unicode.

Tôi sẽ dính vào wchar_tL"".


4
Steven, bạn đang trích dẫn một văn bản được viết bởi một người không hiểu ý nghĩa của từ 'Unicode'. Đó là một trong những tài liệu đáng tiếc từ thời UCS-2 nhầm lẫn.
Pavel Radzivilovsky

2
@PavelRadzivilovsky: Tài liệu được viết cho một hệ thống, trong đó UnicodeUTF-16LE thường được sử dụng thay thế cho nhau. Mặc dù không chính xác về mặt kỹ thuật, nhưng nó vẫn rõ ràng. Điều này cũng được chỉ ra rõ ràng trong phần giới thiệu của cùng một văn bản: "Windows đại diện cho các ký tự Unicode sử dụng bảng mã UTF-16 [...]" .
IInspectable

11

Tôi muốn đề xuất một cách tiếp cận khác (cả hai đều không).

Để tóm tắt, hãy sử dụng char * và std :: string, giả sử mã hóa UTF-8 và chỉ thực hiện chuyển đổi sang UTF-16 khi gói các hàm API.

Bạn có thể tìm thêm thông tin và giải thích cho cách tiếp cận này trong các chương trình Windows tại http://www.utf8everywhere.org .


@PavelRadzivilovsky, khi triển khai đề xuất của bạn trong ứng dụng VC ++, chúng tôi sẽ đặt bộ mã VC ++ thành 'Không có' hoặc 'Nhiềubyte (MBCS)'? Lý do tôi hỏi là tôi vừa cài đặt Boost :: Locale và bộ ký tự mặc định là MBCS. FWIW, ứng dụng ASCII thuần túy của tôi đã được đặt thành 'Không có' và hiện tôi đã đặt nó thành 'MBCS' (vì tôi sẽ sử dụng Boost :: Locale trong đó) và nó hoạt động tốt. Xin hãy tư vấn.
Caroline Beltran

Như utf8everywhere khuyến nghị, tôi sẽ đặt nó thành 'Sử dụng bộ ký tự Unicode'. Quảng cáo này an toàn hơn, nhưng không bắt buộc. Tác giả của Boost :: locale là một người rất thông minh, tôi chắc chắn rằng anh ấy đã làm đúng.
Pavel Radzivilovsky 22/09/2016

1
Câu thần chú UTF-8 Everywhere sẽ không trở thành giải pháp phù hợp, chỉ vì nó được lặp lại thường xuyên hơn. UTF-8 chắc chắn là một mã hóa hấp dẫn để tuần tự hóa (ví dụ: tệp hoặc ổ cắm mạng), nhưng trên Windows, nó thường thích hợp hơn, để lưu trữ dữ liệu ký tự bằng cách sử dụng mã hóa UTF-16 gốc bên trong và chuyển đổi ở ranh giới ứng dụng. Một lý do là UTF-16 là kiểu mã hóa duy nhất có thể được chuyển đổi ngay lập tức sang bất kỳ kiểu mã hóa được hỗ trợ nào khác. Đây không phải là trường hợp của UTF-8.
IInspectable

"..UTF-16 là kiểu mã hóa duy nhất có thể được chuyển đổi ngay lập tức sang bất kỳ kiểu mã hóa được hỗ trợ nào khác." Ý anh là gì? Có vấn đề gì khi chuyển đổi mã hóa UTF-8 sang bất kỳ thứ gì khác?
Pavel Radzivilovsky

1
Tôi không hiểu. Đối với bất cứ điều gì khác - như những gì? Ví dụ: UCS-4? Tại sao không? Có vẻ rất dễ dàng, tất cả các thuật toán số ..
Pavel Radzivilovsky

7

TCHAR/ WCHARcó thể là đủ cho một số dự án kế thừa. Nhưng đối với các ứng dụng mới, tôi sẽ nói KHÔNG .

Tất cả những thứ này TCHAR/ những WCHARthứ này đều có vì lý do lịch sử. TCHARcung cấp một cách có vẻ gọn gàng (ngụy trang) để chuyển đổi giữa mã hóa văn bản ANSI (MBCS) và mã hóa văn bản Unicode (UTF-16). Trong quá khứ, người ta không hiểu biết về số lượng ký tự của tất cả các ngôn ngữ trên thế giới. Họ giả định 2 byte là đủ để đại diện cho tất cả các ký tự và do đó sử dụng lược đồ mã hóa ký tự có độ dài cố định WCHAR. Tuy nhiên, điều này không còn đúng nữa sau khi Unicode 2.0 được phát hành vào năm 1996 .

Đó là để nói: Không có vấn đề mà bạn sử dụng trong CHAR/ WCHAR/ TCHAR, phần xử lý văn bản trong chương trình của bạn sẽ có thể xử lý các ký tự chiều dài biến cho toàn cầu.

Vì vậy, bạn thực sự cần làm nhiều việc hơn là chọn một từ CHAR/ WCHAR/ TCHARđể lập trình trong Windows:

  1. Nếu ứng dụng của bạn nhỏ và không liên quan đến xử lý văn bản (tức là chỉ truyền xung quanh chuỗi văn bản dưới dạng đối số), thì hãy gắn bó với WCHAR. Vì cách này dễ làm việc hơn với WinAPI có hỗ trợ Unicode.
  2. Nếu không, tôi sẽ đề xuất sử dụng UTF-8 làm mã hóa nội bộ và lưu trữ văn bản trong chuỗi char hoặc std :: string. Và giấu chúng thành UTF-16 khi gọi WinAPI. UTF-8 hiện là kiểu mã hóa thống trị và có rất nhiều thư viện và công cụ tiện dụng để xử lý chuỗi UTF-8.

Hãy xem trang web tuyệt vời này để đọc chuyên sâu hơn: http://utf8everywhere.org/


2
"UTF-8 hiện là kiểu mã hóa thống trị" - Điều này đã trở nên sai lầm, khi bỏ qua phần thứ hai của trích dẫn ( "cho World Wide Web" ). Đối với các ứng dụng dành cho máy tính để bàn, mã hóa ký tự gốc được sử dụng nhiều nhất có thể vẫn là UTF-16. Windows sử dụng nó, Mac OS X cũng vậy, và các kiểu chuỗi của .NET và Java cũng vậy. Điều đó chiếm một lượng lớn mã ngoài đó. Đừng hiểu lầm tôi, không có gì sai với UTF-8 để tuần tự hóa. Nhưng thường xuyên hơn không (đặc biệt là trên Windows), bạn sẽ thấy rằng sử dụng UTF-16 nội bộ thích hợp hơn.
IInspectable

4

Phải, chắc chắn rồi; ít nhất là đối với macro _T. Tuy nhiên, tôi không chắc lắm về những thứ có ký tự rộng.

Lý do là để hỗ trợ tốt hơn WinCE hoặc các nền tảng Windows không chuẩn khác. Nếu bạn chắc chắn 100% rằng mã của bạn sẽ vẫn còn trên NT, thì bạn có thể chỉ cần sử dụng khai báo C-string thông thường. Tuy nhiên, tốt nhất là bạn nên hướng tới cách tiếp cận linh hoạt hơn, vì việc # xác định macro đó trên nền tảng không phải windows sẽ dễ dàng hơn nhiều so với việc duyệt qua hàng nghìn dòng mã và thêm nó ở mọi nơi trong trường hợp bạn cần chuyển một số thư viện sang windows mobile.


1
WinCE sử dụng chuỗi wchar_t 16 bit giống như Win32. Chúng tôi có một lượng lớn mã chạy trên WinCE và Win32 và chúng tôi không bao giờ sử dụng TCHAR.
mhenry1384

2

IMHO, nếu có TCHAR trong mã của bạn, bạn đang làm việc ở mức trừu tượng sai.

Sử dụng bất kỳ loại chuỗi nào thuận tiện nhất cho bạn khi xử lý văn bản - đây hy vọng sẽ là thứ hỗ trợ unicode, nhưng điều đó tùy thuộc vào bạn. Thực hiện chuyển đổi ở ranh giới API hệ điều hành nếu cần.

Khi xử lý các đường dẫn tệp, hãy tạo kiểu tùy chỉnh của riêng bạn thay vì sử dụng chuỗi. Điều này sẽ cho phép bạn phân tách đường dẫn độc lập với hệ điều hành, sẽ cung cấp cho bạn giao diện mã dễ dàng hơn so với việc ghép và tách chuỗi thủ công và sẽ dễ dàng hơn rất nhiều để thích ứng với các hệ điều hành khác nhau (ansi, ucs-2, utf-8, bất cứ điều gì) .


Unicode có ít nhất ba mã hóa hiện tại (UTF-8, UTF-16, UTF-32) và một mã hóa không dùng nữa (UCS-2, một tập hợp con của những gì hiện là UTF-16). Bạn tham khảo cái nào? Tôi thích phần còn lại của các đề xuất mặc dù +1
0xC0000022L

2

Lý do duy nhất tôi thấy để sử dụng bất kỳ thứ gì khác ngoài WCHAR rõ ràng là tính di động và hiệu quả.

Nếu bạn muốn thực thi cuối cùng của mình càng nhỏ càng tốt, hãy sử dụng char.

Nếu bạn không quan tâm đến việc sử dụng RAM và muốn quốc tế hóa dễ dàng như bản dịch đơn giản, hãy sử dụng WCHAR.

Nếu bạn muốn làm cho mã của mình linh hoạt, hãy sử dụng TCHAR.

Nếu bạn chỉ định sử dụng các ký tự Latinh, bạn cũng có thể sử dụng chuỗi ASCII / MBCS để người dùng của bạn không cần nhiều RAM.

Đối với những người "i18n ngay từ đầu", hãy tiết kiệm cho mình không gian mã nguồn và chỉ cần sử dụng tất cả các chức năng của Unicode.


-1

Chỉ thêm vào một câu hỏi cũ:

KHÔNG

Bắt đầu một dự án CLR C ++ mới trong VS2010. Chính Microsoft sử dụng L"Hello World", 'nuff nói.


13
CLR là một môi trường rất khác so với mã không được quản lý. Đó không phải là một cuộc tranh cãi.
Cody Grey

3
Ngay cả Microsoft cũng mắc sai lầm.
Pavel Radzivilovsky

6
-1 Câu hỏi được gắn thẻ CC++. Các câu trả lời luôn có thể bị xóa bởi các tác giả tương ứng của họ. Đây sẽ là thời điểm tốt để sử dụng điều khoản đó.
IInspectable

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.