2008-10-16 45 views
11

Tôi đang cố gắng viết một tệp tin wstring vào tệp với dòng ở chế độ nhị phân, nhưng tôi nghĩ rằng tôi đang làm điều gì sai. Đây là những gì tôi đã cố gắng:Viết utf16 vào tập tin ở chế độ nhị phân

ofstream outFile("test.txt", std::ios::out | std::ios::binary); 
wstring hello = L"hello"; 
outFile.write((char *) hello.c_str(), hello.length() * sizeof(wchar_t)); 
outFile.close(); 

Mở test.txt trong ví dụ Firefox với mã hóa thiết lập để UTF16 nó sẽ hiển thị như sau:

chào

Có ai cho tôi biết tại sao điều này xảy ra không?

EDIT:

Mở tệp trong một trình soạn thảo hex tôi nhận được:

FF FE 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 6F 00 00 00 

Hình như tôi nhận được hai byte thêm ở giữa mỗi nhân vật đối với một số lý do?

+0

Thêm một khía cạnh để các địa phương liên quan đến việc dòng để làm việc chuyển đổi từ wchar_t để đầu ra chính xác. Xem bên dưới. –

Trả lời

6

Tôi nghi ngờ sizeof (wchar_t) là 4 trong môi trường của bạn - tức là nó ghi ra UTF-32/UCS-4 thay vì UTF-16. Đó chắc chắn là những gì các bãi chứa hex trông giống như.

Thật dễ dàng, đủ để kiểm tra (chỉ cần in ra sizeof (wchar_t)) nhưng tôi khá chắc chắn đó là những gì đang xảy ra.

Để chuyển từ chuỗi UTF-32 sang UTF-16, bạn cần áp dụng mã hóa thích hợp, khi cặp thay thế được phát.

+0

Vâng, bạn là wchar_t đúng là kích thước 4, tôi đang ở trong một mac. Vì vậy, điều đó giải thích rất nhiều :) Tôi biết về các cặp thay thế trong UTF-16, sẽ phải nhìn vào đó nhiều hơn một chút. – Cactuar

+0

Từ đầu ra bạn không thể nói nó là UTF-16 hoặc UTF-32 tất cả nó cho thấy là wchar_t là 4 byte rộng. Mã hóa của chuỗi không được định nghĩa bởi ngôn ngữ (mặc dù nó có nhiều khả năng là UCS-4). –

0

Bạn nên xem tệp đầu ra trong trình chỉnh sửa hex chẳng hạn như WinHex để bạn có thể xem các bit và byte thực, để xác minh rằng đầu ra thực sự là UTF-16. Đăng nó ở đây và cho chúng tôi biết kết quả. Điều đó sẽ cho chúng tôi biết có nên đổ lỗi cho Firefox hay chương trình C++ của bạn hay không.

Nhưng có vẻ như tôi thích chương trình C++ của bạn và Firefox không diễn giải đúng UTF-16 của bạn. UTF-16 gọi hai byte cho mỗi ký tự. Tuy nhiên, Firefox đang in nhiều gấp đôi số ký tự, vì vậy có lẽ nó đang cố giải thích chuỗi của bạn là UTF-8 hoặc ASCII, thường chỉ có 1 byte cho mỗi ký tự.

Khi bạn nói "Firefox có mã hóa được đặt thành UTF16", ý của bạn là gì? Tôi hoài nghi rằng công việc đó.

14

Ở đây chúng tôi chạy vào các thuộc tính ngôn ngữ được sử dụng ít. Nếu bạn xuất chuỗi của mình dưới dạng chuỗi (thay vì dữ liệu thô), bạn có thể lấy ngôn ngữ để thực hiện chuyển đổi thích hợp một cách tự động.

N.B. Mã này không tính đến tính edianness của ký tự wchar_t.

#include <locale> 
#include <fstream> 
#include <iostream> 
// See Below for the facet 
#include "UTF16Facet.h" 

int main(int argc,char* argv[]) 
{ 
    // construct a custom unicode facet and add it to a local. 
    UTF16Facet *unicodeFacet = new UTF16Facet(); 
    const std::locale unicodeLocale(std::cout.getloc(), unicodeFacet); 

    // Create a stream and imbue it with the facet 
    std::wofstream saveFile; 
    saveFile.imbue(unicodeLocale); 


    // Now the stream is imbued we can open it. 
    // NB If you open the file stream first. Any attempt to imbue it with a local will silently fail. 
    saveFile.open("output.uni"); 
    saveFile << L"This is my Data\n"; 


    return(0); 
}  

File: UTF16Facet.h

#include <locale> 

class UTF16Facet: public std::codecvt<wchar_t,char,std::char_traits<wchar_t>::state_type> 
{ 
    typedef std::codecvt<wchar_t,char,std::char_traits<wchar_t>::state_type> MyType; 
    typedef MyType::state_type   state_type; 
    typedef MyType::result    result; 


    /* This function deals with converting data from the input stream into the internal stream.*/ 
    /* 
    * from, from_end: Points to the beginning and end of the input that we are converting 'from'. 
    * to, to_limit: Points to where we are writing the conversion 'to' 
    * from_next:  When the function exits this should have been updated to point at the next location 
    *     to read from. (ie the first unconverted input character) 
    * to_next:   When the function exits this should have been updated to point at the next location 
    *     to write to. 
    * 
    * status:   This indicates the status of the conversion. 
    *     possible values are: 
    *     error:  An error occurred the bad file bit will be set. 
    *     ok:   Everything went to plan 
    *     partial: Not enough input data was supplied to complete any conversion. 
    *     nonconv: no conversion was done. 
    */ 
    virtual result do_in(state_type &s, 
          const char *from,const char *from_end,const char* &from_next, 
          wchar_t  *to, wchar_t *to_limit,wchar_t* &to_next) const 
    { 
     // Loop over both the input and output array/ 
     for(;(from < from_end) && (to < to_limit);from += 2,++to) 
     { 
      /*Input the Data*/ 
      /* As the input 16 bits may not fill the wchar_t object 
      * Initialise it so that zero out all its bit's. This 
      * is important on systems with 32bit wchar_t objects. 
      */ 
      (*to)        = L'\0'; 

      /* Next read the data from the input stream into 
      * wchar_t object. Remember that we need to copy 
      * into the bottom 16 bits no matter what size the 
      * the wchar_t object is. 
      */ 
      reinterpret_cast<char*>(to)[0] = from[0]; 
      reinterpret_cast<char*>(to)[1] = from[1]; 
     } 
     from_next = from; 
     to_next  = to; 

     return((from > from_end)?partial:ok); 
    } 



    /* This function deals with converting data from the internal stream to a C/C++ file stream.*/ 
    /* 
    * from, from_end: Points to the beginning and end of the input that we are converting 'from'. 
    * to, to_limit: Points to where we are writing the conversion 'to' 
    * from_next:  When the function exits this should have been updated to point at the next location 
    *     to read from. (ie the first unconverted input character) 
    * to_next:   When the function exits this should have been updated to point at the next location 
    *     to write to. 
    * 
    * status:   This indicates the status of the conversion. 
    *     possible values are: 
    *     error:  An error occurred the bad file bit will be set. 
    *     ok:   Everything went to plan 
    *     partial: Not enough input data was supplied to complete any conversion. 
    *     nonconv: no conversion was done. 
    */ 
    virtual result do_out(state_type &state, 
          const wchar_t *from, const wchar_t *from_end, const wchar_t* &from_next, 
          char   *to, char   *to_limit, char*   &to_next) const 
    { 
     for(;(from < from_end) && (to < to_limit);++from,to += 2) 
     { 
      /* Output the Data */ 
      /* NB I am assuming the characters are encoded as UTF-16. 
      * This means they are 16 bits inside a wchar_t object. 
      * As the size of wchar_t varies between platforms I need 
      * to take this into consideration and only take the bottom 
      * 16 bits of each wchar_t object. 
      */ 
      to[0]  = reinterpret_cast<const char*>(from)[0]; 
      to[1]  = reinterpret_cast<const char*>(from)[1]; 

     } 
     from_next = from; 
     to_next  = to; 

     return((to > to_limit)?partial:ok); 
    } 
}; 
+0

Lưu ý rằng Facet của bạn thực hiện chuyển đổi đến/từ UCS-2, chứ không phải UTF-16. UTF-16 là một chiều dài biến đổi mã hóa mà các công cụ được gọi là cặp thay thế. UCS-2 là một tập hợp con của Unicode, đó là lý do UTF-16 đã được phát minh. –

2

Trên cửa sổ sử dụng wofstream và khía cạnh UTF16 định nghĩa ở trên không bởi vì các wofstream chuyển đổi tất cả các byte với 0A giá trị cho 2 byte 0D 0A, đây là không phân biệt về cách bạn truyền byte 0A vào, '\ x0A', L '\ x0A', L '\ x000A', '\ n', L '\ n' và std :: endl cho kết quả tương tự. Trên cửa sổ, bạn phải mở tập tin với một dòng (không phải là một wofsteam) trong chế độ nhị phân và viết đầu ra giống như nó được thực hiện trong bài gốc.

1

Đã cung cấp Utf16Facet không hoạt động trong gcc cho các chuỗi lớn, đây là phiên bản hoạt động cho tôi ... Cách này tệp sẽ được lưu trong UTF-16LE. Đối với UTF-16BE, chỉ cần đảo ngược các bài tập trong do_indo_out, ví dụ: to[0] = from[1]to[1] = from[0]

#include <locale> 
#include <bits/codecvt.h> 


class UTF16Facet: public std::codecvt<wchar_t,char,std::char_traits<wchar_t>::state_type> 
{ 
    typedef std::codecvt<wchar_t,char,std::char_traits<wchar_t>::state_type> MyType; 
    typedef MyType::state_type   state_type; 
    typedef MyType::result    result; 


    /* This function deals with converting data from the input stream into the internal stream.*/ 
    /* 
    * from, from_end: Points to the beginning and end of the input that we are converting 'from'. 
    * to, to_limit: Points to where we are writing the conversion 'to' 
    * from_next:  When the function exits this should have been updated to point at the next location 
    *     to read from. (ie the first unconverted input character) 
    * to_next:   When the function exits this should have been updated to point at the next location 
    *     to write to. 
    * 
    * status:   This indicates the status of the conversion. 
    *     possible values are: 
    *     error:  An error occurred the bad file bit will be set. 
    *     ok:   Everything went to plan 
    *     partial: Not enough input data was supplied to complete any conversion. 
    *     nonconv: no conversion was done. 
    */ 
    virtual result do_in(state_type &s, 
          const char *from,const char *from_end,const char* &from_next, 
          wchar_t  *to, wchar_t *to_limit,wchar_t* &to_next) const 
    { 

     for(;from < from_end;from += 2,++to) 
     { 
      if(to<=to_limit){ 
       (*to)        = L'\0'; 

       reinterpret_cast<char*>(to)[0] = from[0]; 
       reinterpret_cast<char*>(to)[1] = from[1]; 

       from_next = from; 
       to_next  = to; 
      } 
     } 

     return((to != to_limit)?partial:ok); 
    } 



    /* This function deals with converting data from the internal stream to a C/C++ file stream.*/ 
    /* 
    * from, from_end: Points to the beginning and end of the input that we are converting 'from'. 
    * to, to_limit: Points to where we are writing the conversion 'to' 
    * from_next:  When the function exits this should have been updated to point at the next location 
    *     to read from. (ie the first unconverted input character) 
    * to_next:   When the function exits this should have been updated to point at the next location 
    *     to write to. 
    * 
    * status:   This indicates the status of the conversion. 
    *     possible values are: 
    *     error:  An error occurred the bad file bit will be set. 
    *     ok:   Everything went to plan 
    *     partial: Not enough input data was supplied to complete any conversion. 
    *     nonconv: no conversion was done. 
    */ 
    virtual result do_out(state_type &state, 
          const wchar_t *from, const wchar_t *from_end, const wchar_t* &from_next, 
          char   *to, char   *to_limit, char*   &to_next) const 
    { 

     for(;(from < from_end);++from, to += 2) 
     { 
      if(to <= to_limit){ 

       to[0]  = reinterpret_cast<const char*>(from)[0]; 
       to[1]  = reinterpret_cast<const char*>(from)[1]; 

       from_next = from; 
       to_next  = to; 
      } 
     } 

     return((to != to_limit)?partial:ok); 
    } 
}; 
6

Nó rất dễ dàng nếu bạn sử dụng các tiêu chuẩn C++11 (vì có rất nhiều bổ sung bao gồm như "utf8" mà giải quyết vấn đề này mãi mãi).

Nhưng nếu bạn muốn sử dụng mã đa nền tảng với các tiêu chuẩn cũ hơn, bạn có thể sử dụng phương pháp này để viết với suối:

  1. Read the article about UTF converter for streams
  2. Thêm stxutif.h để dự án của bạn từ các nguồn trên
  3. Mở tệp ở chế độ ANSI và thêm BOM vào đầu tệp, như sau:

    std::ofstream fs; 
    fs.open(filepath, std::ios::out|std::ios::binary); 
    
    unsigned char smarker[3]; 
    smarker[0] = 0xEF; 
    smarker[1] = 0xBB; 
    smarker[2] = 0xBF; 
    
    fs << smarker; 
    fs.close(); 
    
  4. Sau đó mở file như UTF và viết nội dung của bạn có:

    std::wofstream fs; 
    fs.open(filepath, std::ios::out|std::ios::app); 
    
    std::locale utf8_locale(std::locale(), new utf8cvt<false>); 
    fs.imbue(utf8_locale); 
    
    fs << .. // Write anything you want... 
    
+1

'+ 1' để tham khảo và giải thích rõ ràng :) – Anne

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