回答編集履歴
2
LinuxでC/C++のロケール独立性調査した内容を追記
test
CHANGED
@@ -277,3 +277,47 @@
|
|
277
277
|
|
278
278
|
http://www17.plala.or.jp/KodamaDeveloped/LetsProgramming/details_how_to_develop_japanese_application_codecvt_libstdcpp_source.html
|
279
279
|
|
280
|
+
### LinuxでC/C++のロケール独立性調査
|
281
|
+
|
282
|
+
LinuxでCと同期したままimbueに別のロケールを設定してみたり、同期を切ってみたりしてみた。
|
283
|
+
|
284
|
+
```bash
|
285
|
+
cat >test_imbue_different_from_c.cpp <<EOF
|
286
|
+
#include <iostream>
|
287
|
+
using namespace std;
|
288
|
+
void test() {
|
289
|
+
// ja_JP.SJIS ... シフトJISをこの名前でロケール追加した環境です。
|
290
|
+
// ja_JP.EUC-JP ... EUCをこの名前でロケール追加した環境です。
|
291
|
+
std::setlocale(LC_CTYPE, "ja_JP.SJIS");
|
292
|
+
wcout.imbue(locale("ja_JP.EUC-JP"));
|
293
|
+
wcout << L"日本語" << endl;
|
294
|
+
}
|
295
|
+
int main() {
|
296
|
+
test();
|
297
|
+
ios_base::sync_with_stdio(false);
|
298
|
+
test();
|
299
|
+
return 0;
|
300
|
+
}
|
301
|
+
EOF
|
302
|
+
g++ -g -Wall test_imbue_different_from_c.cpp -o test_imbue_different_from_c
|
303
|
+
for enc in "cp932" "euc-jp"; do
|
304
|
+
echo "[$enc]"
|
305
|
+
./test_imbue_different_from_c | iconv -c -f $enc
|
306
|
+
done
|
307
|
+
```
|
308
|
+
|
309
|
+
#### 結果
|
310
|
+
```bash
|
311
|
+
[cp932]
|
312
|
+
日本語
|
313
|
+
ニヒワク
|
314
|
+
[euc-jp]
|
315
|
+
{
|
316
|
+
日本語
|
317
|
+
```
|
318
|
+
|
319
|
+
#### 考察
|
320
|
+
同期している場合、C側でワイド→マルチバイト文字列変換をしているので、シフトJISで出ていて、imbueは効いていない。
|
321
|
+
同期していない場合、C++だけで処理しているため、ワイド→マルチバイト文字列変換にimbueのロケールが効いている。
|
322
|
+
|
323
|
+
MINGW64ではimbueに"C"ロケール以外設定できないので、同期していない場合日本語などのエンコーディングは指定できない。すると同期を切ったときにロケールの設定手段がなくなってしまうので、とりあえずC言語のロケール設定を使っているのかもしれない。またWindowsでは_writeがテキストモードで端末に合わせたエンコーディング変換を行っている挙動を確認したので、ロケール未設定("C")のデメリットが大きいと踏んだのかもしれない。独自のCRTを持てないMINGW64では--enable-clocale=genericにせざるをえない。結果C言語のロケール設定に連動することを許容したのではないかと思う。
|
1
std::ios_base::sync_with_stdioについて追記
test
CHANGED
@@ -195,3 +195,85 @@
|
|
195
195
|
UCRTの出力調査としては十分だと思うので、次からは最初の回答に書いた``std::ios_base::sync_with_stdio(false);``とcin/wcin周りをVC++やLinuxも合わせて見てみる。
|
196
196
|
|
197
197
|
これらが終われば、MINGW64/UCRT64での基本的な使い方が分かるはず。
|
198
|
+
|
199
|
+
---
|
200
|
+
|
201
|
+
## MINGW64での日本語出力について
|
202
|
+
|
203
|
+
ここからはMINGW64(MSVCRT)メインに話を戻します。
|
204
|
+
まずはstdioとの同期を切るだけでwcoutが使える事実に基づき、`std::ios_base::sync_with_stdio`を調査しました。
|
205
|
+
|
206
|
+
### std::ios_base::sync_with_stdioについて
|
207
|
+
|
208
|
+
https://cpprefjp.github.io/reference/ios/ios_base/sync_with_stdio.html
|
209
|
+
https://en.cppreference.com/w/cpp/io/ios_base/sync_with_stdio
|
210
|
+
|
211
|
+
C/C++でバッファを同期するかどうかで、実装方法については特別に規定はないありません。ただし、Linux gccだと以下のような動作になっているようです。
|
212
|
+
|
213
|
+
- 同期する場合はCの関数を経由してシステムコールから出力される
|
214
|
+
- 同期しない場合は直接システムコールから出力される
|
215
|
+
|
216
|
+
なので同期しない場合ワイド文字のストリームはデフォルトロケール("C")だと出力できなくなります。
|
217
|
+
確認用のスクリプトが以下になります。
|
218
|
+
|
219
|
+
```bash
|
220
|
+
for sync_flag in "true" "false"; do
|
221
|
+
for out in "cout" "wcout"; do
|
222
|
+
echo "[${sync_flag}-${out}]"
|
223
|
+
prefix=""
|
224
|
+
if [ "$out" == "wcout" ]; then
|
225
|
+
prefix="L"
|
226
|
+
fi
|
227
|
+
exefile="test_sync_with_stdio_${sync_flag}_${out}"
|
228
|
+
cat >"${exefile}.cpp" <<EOF
|
229
|
+
#include<iostream>
|
230
|
+
using namespace std;
|
231
|
+
int main() {
|
232
|
+
::setlocale(LC_CTYPE, "");
|
233
|
+
ios_base::sync_with_stdio(${sync_flag});
|
234
|
+
${out} << ${prefix}"abc日本語" << endl;
|
235
|
+
return 0;
|
236
|
+
}
|
237
|
+
EOF
|
238
|
+
g++ -g -Wall ${exefile}.cpp -o ${exefile}
|
239
|
+
gdb ./${exefile} <<EOF
|
240
|
+
r
|
241
|
+
b write
|
242
|
+
r
|
243
|
+
where
|
244
|
+
delete 1
|
245
|
+
c
|
246
|
+
q
|
247
|
+
EOF
|
248
|
+
done
|
249
|
+
done
|
250
|
+
```
|
251
|
+
|
252
|
+
### MINGW64で同じことをしてみる
|
253
|
+
|
254
|
+
先のスクリプトをちょっと置換して使用する(システムコールが違うので)
|
255
|
+
```bash
|
256
|
+
$ sed -i 's/write/WriteFile/g' test_sync_with_stdio.sh
|
257
|
+
```
|
258
|
+
|
259
|
+
結果は以下のとおり。
|
260
|
+
|
261
|
+
|環境/処理系|std::ios_base::sync_with_stdio|出力|CRT入り口|
|
262
|
+
|:--|:--:|:--:|:--:|
|
263
|
+
|linux|true|cout|_IO_new_file_overflow(fputc)|
|
264
|
+
|linux|true|wcout|__GI_putwc(fputwc)|
|
265
|
+
|linux|false|cout|__GI___libc_write(write)|
|
266
|
+
|linux|false|wcout|止まらない|
|
267
|
+
|mingw64|true|cout|msvcrt!fwrite|
|
268
|
+
|mingw64|true|wcout|msvcrt!fputwc|
|
269
|
+
|mingw64|false|cout|msvcrt!_write|
|
270
|
+
|mingw64|false|wcout|msvcrt!_write|
|
271
|
+
|
272
|
+
LinuxとMINGW64で比較的似ているようにも見えるが、MINGW64では同期せずwcoutのとき出力されているし、_writeはWriteFileのラッパーではなく、文字コード変換を含む重量級の関数として実装されている。
|
273
|
+
|
274
|
+
デフォルトのwcoutは"C"ロケール設定なので、同期しない(=ワイド→マルチバイトをC++側でやる)場合もASCII以外がマルチバイト文字列にならないはずだが(LinuxではASCIIすら出力されていない)、MINGW64ではなぜかC言語側のロケール設定を使って変換されている。本来はimbueで設定されたロケールに従い、C言語側と同様の方法で出力されるべきだと思う(ロケール関連は基本実装依存だが)。
|
275
|
+
|
276
|
+
なお、C言語側のロケール設定を使って変換されるのはMINGW64のgccのconfigで--enable-clocaleにgenericが選ばれたため。Linuxではgnuが選ばれている。
|
277
|
+
|
278
|
+
http://www17.plala.or.jp/KodamaDeveloped/LetsProgramming/details_how_to_develop_japanese_application_codecvt_libstdcpp_source.html
|
279
|
+
|