2010-02-23 41 views
14

Tôi hiện đang làm việc trên một chương trình MFC đặc biệt phải làm việc với UTF-8. Tại một số điểm, tôi phải ghi dữ liệu UTF-8 vào một tập tin; để làm điều đó, tôi đang sử dụng CFiles và CStrings.UTF-8, CString và CFile? (C++, MFC)

Khi tôi nhận được để viết utf-8 (nhân vật Nga, để được chính xác hơn) dữ liệu vào một tập tin, đầu ra trông giống như

Ðàñïå÷àòàíî: 
Ñèñòåìà 
Ïðîèçâîäñòâî 

và vv Đây là assurely không utf-8. Để đọc dữ liệu này đúng cách, tôi phải thay đổi cài đặt hệ thống của mình; thay đổi các ký tự không phải ASCII thành bảng mã hóa tiếng Nga không hoạt động, nhưng sau đó tất cả các ký tự không phải ascii dựa trên latin của tôi đều thất bại. Dù sao, đó là cách tôi làm điều đó.

CFile CSVFile(m_sCible, CFile::modeCreate|CFile::modeWrite); 
CString sWorkingLine; 
//Add stuff into sWorkingline 
CSVFile.Write(sWorkingLine,sWorkingLine.GetLength()); 
//Clean sWorkingline and start over 

Tôi có thiếu gì đó không? Tôi sẽ sử dụng cái gì khác thay thế? Có một số loại bắt tôi đã bỏ lỡ? Tôi sẽ được điều chỉnh cho sự khôn ngoan và kinh nghiệm của bạn, các lập trình viên đồng nghiệp.

EDIT: Tất nhiên, như tôi vừa hỏi một câu hỏi, cuối cùng tôi tìm thấy điều gì đó có thể thú vị, có thể được tìm thấy here. Nghĩ rằng tôi có thể chia sẻ nó.

EDIT 2:

Được rồi, vì vậy tôi thêm BOM hồ sơ của tôi, mà bây giờ chứa ký tự chineese, có lẽ bởi vì tôi đã không chuyển đổi dòng của tôi sang UTF-8. Để thêm các bom tôi đã làm ...

char BOM[3]={0xEF, 0xBB, 0xBF}; 
CSVFile.Write(BOM,3); 

Và sau đó, tôi đã thêm ...

TCHAR TestLine; 
    //Convert the line to UTF-8 multibyte. 
    WideCharToMultiByte (CP_UTF8,0,sWorkingLine,sWorkingLine.GetLength(),TestLine,strlen(TestLine)+1,NULL,NULL); 
    //Add the line to file. 
    CSVFile.Write(TestLine,strlen(TestLine)+1); 

Nhưng sau đó tôi không thể biên dịch, như tôi không thực sự biết làm thế nào để có được chiều dài của TestLine. strlen dường như không chấp nhận TCHAR. Đã sửa lỗi, sử dụng chiều dài tĩnh là 1000 thay thế.

EDIT 3:

Vì vậy, tôi đã thêm mã này ...

wchar_t NewLine[1000]; 
    wcscpy(NewLine, CT2CW((LPCTSTR) sWorkingLine)); 
    TCHAR* TCHARBuf = new TCHAR[1000]; 

    //Convert the line to UTF-8 multibyte. 
    WideCharToMultiByte (CP_UTF8,0,NewLine,1000,TCHARBuf,1000,NULL,NULL); 

    //Find how many characters we have to add 
    size_t size = 0; 
    HRESULT hr = StringCchLength(TCHARBuf, MAX_PATH, &size); 

    //Add the line to the file 
    CSVFile.Write(TCHARBuf,size); 

Nó biên dịch tốt, nhưng khi tôi đi xem thử tập tin mới của tôi, đó là chính xác giống như khi tôi didn' t có tất cả mã mới này (ví dụ: Ðàñïå ÷ àòàíî :). Có vẻ như tôi đã không tiến lên một bước, mặc dù tôi đoán chỉ một điều nhỏ là những gì ngăn cách tôi khỏi chiến thắng.

EDIT 4:

trước đây tôi gỡ bỏ thêm mã, như Nate hỏi, và tôi quyết định sử dụng mã của mình thay vào đó, có nghĩa là bây giờ, khi tôi nhận được để thêm dòng của tôi, tôi có ...

 CT2CA outputString(sWorkingLine, CP_UTF8); 

    //Add line to file. 
    CSVFile.Write(outputString,::strlen(outputString)); 

Mọi thứ biên soạn tốt, nhưng các ký tự tiếng Nga được hiển thị là ???????. Đến gần hơn, nhưng vẫn không phải thế. Btw, tôi muốn cảm ơn tất cả những người đã cố gắng/cố gắng giúp tôi, nó được đánh giá cao. Tôi đã bị mắc kẹt trong thời gian này, tôi không thể chờ đợi vấn đề này biến mất.

EDIT cuối cùng (Tôi hy vọng) Bằng cách thay đổi cách đầu tiên tôi nhận được các ký tự UTF-8 (tôi đã mã hóa mà không thực sự biết), đó là sai lầm với cách mới của tôi xuất văn bản, tôi nhận được kết quả chấp nhận được.Bằng cách thêm char UTF-8 BOM vào đầu tệp của tôi, nó có thể được đọc dưới dạng Unicode trong các chương trình khác, như Excel.

Hoan hô! Cảm ơn mọi người!

+1

Bạn sẽ cần sử dụng _tcslen để nhận độ dài cho chuỗi TCHAR. Giống như: \t TCHAR * testTCHAR = _T ("test"); \t int tcharLength = _tcslen (testTCHAR); –

+1

Ngoài ra, nếu bạn cần chuyển đổi chuỗi CString thành chuỗi TCHAR *, hãy thử điều này \t CString testCString = _T ("test"); \t TCHAR * testTCHAR = testCString.GetBuffer(); –

+0

Bạn đang làm việc quá vất vả. Sử dụng 'CT2CA' với tham số thứ hai của' CP_UTF8'. Xem bài đăng của tôi dưới đây. – Nate

Trả lời

24

Khi bạn xuất dữ liệu bạn cần làm (điều này giả định bạn đang soạn thảo trong chế độ Unicode, mà là rất khuyến khích):

CString russianText = L"Привет мир"; 

CFile yourFile(_T("yourfile.txt"), CFile::modeWrite | CFile::modeCreate); 

CT2CA outputString(russianText, CP_UTF8); 
yourFile.Write(outputString, ::strlen(outputString)); 

Nếu _UNICODE không được định nghĩa (bạn đang làm việc trong chế độ multi-byte thay vì), bạn cần biết trang văn bản đầu vào của bạn là gì và chuyển đổi nó thành thứ gì đó bạn có thể sử dụng. Ví dụ này cho thấy làm việc với văn bản tiếng Nga có nghĩa là ở định dạng UTF-16, tiết kiệm nó sang UTF-8:

// Example 1: convert from Russian text in UTF-16 (note the "L" 
// in front of the string), into UTF-8. 
CW2A russianTextAsUtf8(L"Привет мир", CP_UTF8); 
yourFile.Write(russianTextAsUtf8, ::strlen(russianTextAsUtf8)); 

Nhiều khả năng, văn bản tiếng Nga của bạn là trong một số trang mã khác, chẳng hạn như KOI-8R. Trong trường hợp đó, bạn cần chuyển đổi từ trang mã khác sang UTF-16. Sau đó chuyển đổi UTF-16 thành UTF-8. Bạn không thể chuyển đổi trực tiếp từ KOI-8R sang UTF-8 bằng cách sử dụng các macro chuyển đổi vì chúng luôn cố gắng chuyển đổi văn bản thu hẹp sang trang mã hệ thống. Vì vậy, cách dễ dàng là làm điều này:

// Example 2: convert from Russian text in KOI-8R (code page 20866) 
// to UTF-16, and then to UTF-8. Conversions between UTFs are 
// lossless. 
CA2W russianTextAsUtf16("\xf0\xd2\xc9\xd7\xc5\xd4 \xcd\xc9\xd2", 20866); 
CW2A russianTextAsUtf8(russianTextAsUtf16, CP_UTF8); 
yourFile.Write(russianTextAsUtf8, ::strlen(russianTextAsUtf8)); 

Bạn không cần BOM (tùy chọn; Tôi sẽ không sử dụng nó trừ khi có lý do cụ thể để làm như vậy).

Đảm bảo bạn đọc số này: http://msdn.microsoft.com/en-us/library/87zae4a3(VS.80).aspx. Nếu bạn sử dụng sai CT2CA (ví dụ, sử dụng toán tử gán), bạn sẽ gặp rắc rối. Trang tài liệu được liên kết hiển thị các ví dụ về cách sử dụng và cách không sử dụng nó.

Thông tin thêm:

  • Các C trong CT2CA chỉ const. Tôi sử dụng nó khi có thể, nhưng một số chuyển đổi chỉ hỗ trợ phiên bản không const (ví dụ: CW2A).
  • Các T trong CT2CA chỉ ra rằng bạn đang chuyển đổi từ một LPCTSTR. Vì vậy, nó sẽ làm việc cho dù mã của bạn được biên dịch với cờ _UNICODE hay không. Bạn cũng có thể sử dụng CW2A (trong đó W cho biết ký tự rộng).
  • A trong CT2CA cho biết rằng bạn đang chuyển đổi thành chuỗi "ANSI" (8 bit).
  • Cuối cùng, tham số thứ hai là CT2CA cho biết trang mã bạn đang chuyển đổi.

Để làm được việc chuyển đổi ngược lại (từ UTF-8 để LPCTSTR), bạn có thể làm:

CString myString(CA2CT(russianText, CP_UTF8)); 

Trong trường hợp này, chúng tôi đang chuyển đổi từ một "ANSI" chuỗi trong UTF-8 định dạng, tới LPCTSTR. LPCTSTR luôn được giả định là UTF-16 (nếu _UNICODE được xác định) hoặc trang mã hệ thống hiện tại (nếu _UNICODE không được xác định).

+0

Tôi đã thử những gì bạn đã nói: Tôi đã xóa BOM và thay đổi mã của tôi cho bạn. Bây giờ, các ký tự được biểu diễn như ??????? ??. Một cái gì đó vẫn còn thiếu, có lẽ? Tôi sẽ đăng chỉnh sửa. – SeargX

+0

Đại diện là dấu hỏi ở đâu? Nhìn vào tập tin kết quả bằng cách sử dụng một trình soạn thảo hex. Bạn sẽ thấy một cái gì đó như [this] (http://i.imgur.com/RcUsh.png). Và nếu bạn mở nó trong Notepad, bạn sẽ thấy [this] (http://imgur.com/Yl3OU.png). Nếu không, thì văn bản gốc của bạn có thể không được mã hóa chính xác. Hy vọng rằng bạn đang sử dụng '_UNICODE' xác định và đầu vào của bạn là UTF-16. Nếu không, bạn cần phải sử dụng các macro để chuyển đổi từ bất kỳ trang mã nào mà văn bản gốc đang ở, đến trang mã bạn muốn. – Nate

+0

Các dấu hỏi nằm trong tập tin kết quả, và tất cả đều có mã hex dấu hỏi (3F, tôi nghĩ) .Tôi không sử dụng định nghĩa _UNICODE, và tôi không nghĩ đó là một ý hay. Các ký tự tiếng Nga tôi đọc đến từ một tệp XML, mà tôi mở bằng tinyXML, không hỗ trợ UTF-16, chỉ các trang mã hóa UTF-8 và Latin 1. Tôi đoán tôi phải sử dụng các macro, mặc dù tôi không quen thuộc với chúng. – SeargX

6

Bạn sẽ phải chuyển đổi sWorkingLine thành UTF-8 và sau đó ghi nó vào tệp.

WideCharToMultiByte có thể chuyển đổi chuỗi unicode thành UTF-8 nếu bạn chọn mã trang CP_UTF8. MultiByteToWideChar có thể chuyển đổi các ký tự ASCII thành unicode.

+0

Bằng cách sử dụng một hàm như vậy, tất cả văn bản đi kèm sẽ được thay đổi thành nhiều hơn một byte hay chỉ các ký tự không phải ascii? – SeargX

+0

@SeargX, chỉ không phải ascii nếu bạn sử dụng UTF-8. –

+0

D: Hoàn hảo, cảm ơn. @Everyone Tôi nên nhập dữ liệu được chuyển đổi vào loại chuỗi nào? TCHAR? Làm thế nào để xác định độ dài của dòng, cần thiết trong hàm multibytetowidechar? – SeargX

0

Đảm bảo bạn đang sử dụng Unicode (TCHAR là wchar_t). Sau đó, trước khi bạn ghi dữ liệu, hãy chuyển đổi dữ liệu bằng cách sử dụng chức năng API WinCharToMultiByte Win32.

+0

'wchar_t' KHÔNG ** UTF-8 **, nó là ** UCS-2 **. – rhavin