質問
バイナリモードでofstreamを使用してファイルにwstringを書き込もうとしていますが、何か間違っていると思います。これは私が試したものです:
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();
UTF16に設定されたエンコードでFirefoxなどでtest.txtを開くと、次のように表示されます。
h� e� l� l� o�
これがなぜ起こるのか、誰か教えてもらえますか?
編集:
16進エディタでファイルを開く:
FF FE 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 6F 00 00 00
何らかの理由ですべての文字の間に2バイトの余分なバイトがあるように見えますか?
解決
sizeof(wchar_t)は環境内で4であると思われます。つまり、UTF-16ではなくUTF-32 / UCS-4を書き出しています。それは確かに16進ダンプがどのように見えるかです。
これはテストするのに十分簡単です(sizeof(wchar_t)を印刷するだけです)が、それが何が起こっているのかは確かです。
UTF-32 wstringからUTF-16に移行するには、サロゲートペアが作用するため、適切なエンコーディングを適用する必要があります。
他のヒント
ここでは、使用頻度の低いロケールプロパティに遭遇します。 文字列を(生データではなく)文字列として出力する場合、適切な変換を自動的に行うロケールを取得できます。
N.B。このコードでは、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);
}
ファイル: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);
}
};
C ++ 11
標準を使用すると簡単です(この問題を永遠に解決する&quot; utf8&quot;
などの追加インクルードが多数あるため)。
ただし、古い標準でマルチプラットフォームコードを使用する場合は、このメソッドを使用してストリームを記述できます。
- ストリームのUTFコンバーターに関する記事を読む
- 上記のソースからプロジェクトに
stxutif.h
を追加します -
次のように、ANSIモードでファイルを開き、ファイルの先頭にBOMを追加します。
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();
-
次に、ファイルを
UTF
として開き、そこにコンテンツを書き込みます: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...
wofstreamと上記で定義されたutf16ファセットを使用するウィンドウでは、wofstreamは値0Aのすべてのバイトを2バイト0D 0Aに変換するため失敗します。 x0A '、L' \ x000A '、' \ n '、L' \ n '、およびstd :: endlはすべて同じ結果になります。 Windowsでは、バイナリモードでofstream(wofsteamではない)を使用してファイルを開き、元の投稿と同じように出力を書き込む必要があります。
提供された Utf16Facet
は大きな文字列に対して gcc
で機能しませんでした。ここに私のために機能したバージョンがあります...この方法でファイルは< code> UTF-16LE 。 UTF-16BE
の場合、単純に do_in
と do_out
の割り当てを反転します。 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);
}
};
WinHex を使用すると、実際のビットとバイトを確認して、出力が実際にUTF-16であることを確認できます。ここに投稿して、結果をお知らせください。これにより、Firefoxを非難するか、C ++プログラムを非難するかがわかります。
しかし、あなたのC ++プログラムが動作し、FirefoxがUTF-16を正しく解釈していないように見えます。 UTF-16は、すべての文字に対して2バイトを呼び出します。しかし、Firefoxは本来の2倍の文字数を印刷しているので、おそらく文字列をUTF-8またはASCIIとして解釈しようとしています。
「UTF16に設定されたエンコーディングでFirefox」と言うときどういう意味ですか?私はその仕事が機能することに懐疑的です。