2013-10-14 257 views
4

以下是使用std::codecvt_utf8<>方面從wchar_t轉換爲UTF-8的代碼片段。使用Visual Studio 2012,我的期望不符合(請參閱代碼末尾的條件)。我的期望錯了嗎?爲什麼?或者這是一個Visual Studio 2012庫問題?std :: codecvt_utf8方面的問題

#include <locale> 
#include <codecvt> 
#include <cstdlib> 

int main() 
{ 
    std::mbstate_t state = std::mbstate_t(); 
    std::locale loc (std::locale(), new std::codecvt_utf8<wchar_t>); 
    typedef std::codecvt<wchar_t, char, std::mbstate_t> codecvt_type; 
    codecvt_type const & cvt = std::use_facet<codecvt_type> (loc); 

    wchar_t ch = L'\u5FC3'; 
    wchar_t const * from_first = &ch; 
    wchar_t const * from_mid = &ch; 
    wchar_t const * from_end = from_first + 1; 

    char out_buf[1]; 
    char * out_first = out_buf; 
    char * out_mid = out_buf; 
    char * out_end = out_buf + 1; 

    std::codecvt_base::result cvt_res 
     = cvt.out (state, from_first, from_end, from_mid, 
      out_first, out_end, out_mid); 

    // This is what I expect: 
    if (cvt_res == std::codecvt_base::partial 
     && out_mid == out_end 
     && state != 0) 
     ; 
    else 
     abort(); 
} 

這裏的期望是,在一個時間的UTF-8的轉換,但if上述條件的中間的out()函數輸出一個字節是與Visual Studio假2012.

UPDATE

失敗的條件是out_mid == out_endstate != 0條件。基本上,我希望至少生成一個字節,並且可以生成UTF-8序列的下一個字節的必要狀態存儲在變量state中。

回答

4

codecvt::do_outpartial返回碼的標準描述完全這樣說:

在表83:

partial不是所有的源字符轉換

在22.4.1.4.2 [locale.codecvt。虛函數]/5:

返回:枚舉值,如表83的partial返回值,如果(from_next==from_end),表示要麼目的地序列 沒有吸收了所有可用的目標元素,或者在產生另一個目標元素之前需要額外的源元素。

在你的情況,不是所有的(零)源字符進行了改裝,在技術上也不說的輸出序列的內容(「如果」中的句子子句不能進入),但總體來講,「目的地序列沒有吸收所有可用的目標元素「,這裏講的是有效的多字節字符。它們是由codecvt_utf8產生的多字節字符序列的元素

這將是很好有一個更明確的標準寫法,但這裏是兩個證據間接件:

一:舊的C的寬到多字節轉換功能std::wcsrtombs(其特定於語言環境的變體通常是通過codecvt::do_out爲系統提供的語言環境)的現有實現所謂的定義如下:

轉換停止。[...]當下次多字節字符會超過LEN總字節數的限制將被存儲到陣列由dst指出。

兩個,看看codecvt_utf8現有的實現:你已經探索了微軟,這裏是什麼了libC++:codecvt_utf8::do_out這裏呼籲ucs2_to_utf8在Windows和ucs4_to_utf8在其他系統上,並ucs2_to_utf8 does the following(評論我的):

 else if (wc < 0x0800) 
     { 
      // not relevant 
     } 
     else // if (wc <= 0xFFFF) 
     { 
      if (to_end-to_nxt < 3) 
       return codecvt_base::partial; // <- look here 
      *to_nxt++ = static_cast<uint8_t>(0xE0 | (wc >> 12)); 
      *to_nxt++ = static_cast<uint8_t>(0x80 | ((wc & 0x0FC0) >> 6)); 
      *to_nxt++ = static_cast<uint8_t>(0x80 | (wc & 0x003F)); 
     } 

如果輸出序列無法適應消耗一個輸入寬字符導致的多字節字符,則不會寫入輸出序列。

+0

我不確定你想要反駁的是我期望的部分。你能澄清一下嗎? – wilx

+0

@wilx您希望函數產生一個字節,而不是多字節字符。它從來沒有被指定能夠做到這一點,類似的功能,以及現有的實現被指定爲不這樣做。 – Cubbi

+0

假設你是對的,那麼緩衝區應該有多大呢? '的std :: ::的codecvt MAX_LENGTH()'? – wilx

2

儘管沒有直接引用它,但我認爲這是std::codecvt::out最合乎邏輯的行爲。請考慮以下情形:

  • 你以同樣的方式使用std::codecvt::out像你一樣 - 沒有轉化的任何字符(可能不知道)到您的out_buf
  • 你現在要到另一個字符串翻譯成你out_buf(再次使用std::codecvt::out),使得它追加這已經是內部
  • 內容要做到這一點,你決定使用您的buf_mid正如你知道你的字符串後直接指向你在第一步翻譯。
  • 現在,如果std::codecvt::out根據您的期望工作(buf_mid指向第一個字符後),那麼您的out_buf的第一個字符將永遠不會被寫入,這將不會是這種情況下所期望的。

從本質上說,extern_type*& to_next(的std::codecvt::out最後一個參數)是在這裏爲你爲你留下的,其中的一個參考 - 所以你知道在哪裏繼續 - 這是你的情況確實相同的位置,你開始的地方( extern_type* to)參數。

+0

還有'狀態'變量/參數應該給實施一些關於轉換狀態的信息。你上面描述的是恕我直言的應該通過狀態照顧和調用['codecvt :: unshift()'](http://en.cppreference.com/w/cpp/locale/codecvt/unshift)(如果輸出緩衝區只有一個字節長,則可能再次循環),然後再次調用'codecvt :: out()'作爲附加字符串。 – wilx

+0

@wilx這是一個很好的觀點。 Microsoft回答了您的問題了嗎? (我在他們的網站上看到你的問題) –

+0

我還沒有收到任何反應,除了兩個自動化的消息,但。 – wilx