web-dev-qa-db-ja.com

端末がC / C ++プログラムで使用する文字エンコードを決定する方法は?

SyncTERMはデフォルトのMacOSターミナルエミュレータとは異なる文字エンコーディングを使用しており、それらは互いに互換性がないことに気づきました。たとえば、ブロック文字をフォーマット文字列で印刷するとします。 IBM Extended ASCII文字エンコードを使用するSyncTERMでは、\261のような8進エスケープシーケンスを使用します。Terminal.app(およびおそらくiTerm2も)では、これは単に出力します。疑問符。これらの端末はUTF-8を使用するため、\uxxxxエスケープシーケンスを使用する必要があります。

したがって、ASCIIではない特定の文字をフォーマット文字列で印刷し、文字セットに関係なく、すべてのターミナルエミュレータで機能させたいとします。 terminfoデータベースのエントリを使用すると思いますが、terminfoについてはあまり詳しくありません。ここにいくつかのポインタが必要です。

2
user628544

ショート:

  • terminfoはあなたをそこに連れて行かないでしょう、助けにはなりません
  • 端末が実際に使用しているエンコーディングを判断する信頼できる方法はありません
  • ターミナルで使用するエンコーディングがわかっている場合は、Unicodeリテラルから始めるのがよい方法です。
  • ユーザーは、適切なロケールと、端末で実行できるエンコーディングを知っている必要があります。
  • c標準には、Unixライクなプラットフォームで使用できる「ワイド」文字を変換するための関数があります(たとえば、 setlocalewcrtomb および wcsrtombs
2
Thomas Dickey

アプリのロケールをsetlocale(LC_ALL, "")で初期化してから、nl_langinfo(CODESET)を呼び出します。これにより、LANG、LC_CTYPE、LC_ALL環境変数から解決された値が得られます。

これはnotターミナルエミュレータが実際にどのように機能するかを示しますが、これはほとんどすべてのアプリケーションが依存しているものです。これにより誤った結果が得られる場合は、システムが正しく構成されておらず、他のほとんどすべてのアプリもターミナルエミュレーターで正しく機能しません。アプリ開発者として、壊れているかどうかを検出して修正しようとするのはあなたの仕事ではありません。正しく設定されていると安全に想定できます。システム管理者、ディストリビューション開発者、またはシステムをハッキングするユーザーとして、ロケール変数とターミナルエミュレータの実際の動作が一致することを確認するのはあなたの仕事です。

1
egmont

ターミナルエミュレータが適切に設計および構成されている場合、環境変数LC_CTYPEの値がそのエンコーディングと一致する値に設定されていることが保証されます。残念ながら、実際には、LC_CTYPEのチェックは常に信頼できるとは限りません。設定されていないか間違っている可能性があります。 (他の環境変数がロケール設定を伝達する場合があります。詳細については、 ロケールを何に設定する必要がありますか? を参照してください。

どの文字エンコードが可能性が高いかがわかっている場合は、ヒューリスティックを使用してエンコードを判別できる場合があります。異なるエンコーディングで異なる幅のバイト文字列を表示し、カーソルをどれだけ動かすかを調べます。これは、すべての場合に役立つわけではありません。たとえば、シングルバイトエンコーディングを区別することはできません。しかし、可能性のある2つだけがUTF-8と1つのレガシーエンコーディングである場合、それはうまく機能します。 Shellの起動時に、このようにLC_CTYPEを設定し、投稿したスクリプトwidthofを使用して 文字列の表示幅を取得widthof -1は、UTF-8で2文字を表す4バイトの文字列を表示し、印刷可能なlatin-N文字は3バイトのみです。したがって、幅2はUTF-8(または、私にはありそうもない他のマルチバイトエンコーディング)を意味し、幅3はlatin-N(Nを知る方法がない)を意味し、4はシングルバイトエンコーディングを意味します。 128〜159の範囲の印刷可能な文字。

widthof -1
case $? in
  0) export LC_CTYPE=C;; # 7-bit charset
  2) locale_search .utf8 .UTF-8;; # utf8
  3) locale_search .iso88591 .ISO8859-1 .latin1 '';; # 8-bit with nonprintable 128-159, we assume latin1
  4) locale_search .iso88591 .ISO8859-1 .latin1 '';; # some full 8-bit charset, we assume latin1
  *) export LC_CTYPE=C;; # weird charset
esac