私はMSVCでレクサーを書いていますが、128個の基本ラテンUnicode文字すべてに完全に一致する文字を表す方法が必要です。
ただし、このMSDNの記事によると、「0x24と0x40を除いて、0から0x20および0x7fから0x9fの範囲の文字はユニバーサル文字名(UCN)で表すことはできません。」
...これは基本的に、この「許可されていない」文字範囲でswitchステートメントを使用することは言うまでもなく、のようなものを宣言することは許可されていないことを意味します。また、「\n」と「\r」の場合、実際の値/長さはコンパイラ/ターゲットOSによって異なることを理解しています... (つまり、Windowsは「\ r \ n」を使用しますが、Unixは単に「\」を使用しますn 'wchar_t c = '\u0000';
...したがって、適切なエンコードスキームとバイト長が検出され、適切に使用されるようにするために、ユニバーサル文字を使用してこれを回避しました。
しかし、このC3850コンパイラエラーは、私が自分のやり方で物事を行うことを許可することを単に拒否します...では、ソース入力が与えられた場合に適切なエンコーディングスキームと文字の一致を保証
する方法でこれをどのように解決できますか?
2 に答える
C ++ 11では、ユニバーサル文字名で表すことができる文字の制限は、文字および文字列リテラル内には適用されません。
C ++ 11 2.3 / 2
さらに、文字または文字列リテラルのc-char-sequence、s-char-sequence、またはr-char-sequenceの外側にあるユニバーサル文字名の16進値が、 (いずれかの範囲の)制御文字に対応する場合0x00–0x1Fまたは0x7F–0x9F、両方を含む)または基本ソース文字セットの文字に対して、プログラムの形式が正しくありません。15
つまり、UCNに対するこれらの制限は、文字および文字列リテラルの内部には適用されません。
wchar_t c = L'\u0000'; // perfectly okay
switch(c) {
case L'\u0000':
;
}
これはC++03とは異なり、あなたの質問から、Microsoftはこれを許可するようにコンパイラをまだ更新していないと思います。ただし、UCNを使用しても解決しようとしている問題は解決されないため、これは重要ではないと思います。
そのため、適切なエンコードスキームとバイト長が検出され、適切に使用されるようにするために、ユニバーサル文字を使用してこれを回避しました。
UCNを使用しても、使用されるエンコード方式を決定するために何もしません。UCNは、ソースに特定の文字を含めるためのソースエンコーディングに依存しない方法ですが、コンパイラは、その文字が文字通りソースに書き込まれた場合とまったく同じように処理する必要があります。
たとえば、次のコードを考えてみましょう。
int main() {
unsigned char c = 'µ';
std::cout << (int)c << '\n';
}
ソースをUTF-16として保存し、コードページ1252を使用するように構成されたWindowsシステムでMicrosoftのコンパイラを使用してこれをビルドすると、コンパイラは「µ」のUTF-16表現をCP1252表現に変換します。文字を含まない別のコードページで構成されたシステムでこのソースを構築する場合、コンパイラは、文字をそのコードページに変換できないときに警告/エラーを出します。
同様に、ソースコードをUTF-8として(いわゆる「BOM」を使用して、コンパイラがエンコーディングがUTF-8であることを認識できるように)保存すると、文字のUTF-8ソース表現がシステムのUTF-8ソース表現に変換されます。可能であれば、それが何であれ、コードページ。
また、「µ」をUCN「\ u00B5」に置き換えても、コンパイラはまったく同じことを実行します。可能であれば、UCNをシステムのU + 00B5MICROSIGNのコードページ表現に変換します。
では、ソース入力が与えられた場合に適切なエンコードスキームと文字の一致を保証する方法でこれをどのように解決できますか?
何を求めているのかわかりません。char
またはwchar_t
変数/リテラルの整数値が特定のエンコードスキーム(ASCII範囲の文字についてのみ質問しているため、おそらくASCII)と一致していることを確認したいと思いますが、「ソース入力」とは何ですか?レクサーのソースファイルのエンコーディング?レクサーへの入力のエンコーディング?「ソース入力」はどのように変化すると思いますか?
また、「\n」と「\r」の場合、実際の値/長さはコンパイラ/ターゲットOSによって異なることを理解しています...(つまり、Windowsは「\ r \ n」を使用しますが、Unixは単に「\」を使用しますn'および古いバージョンのMacOSは'\r'を使用します)
これは、テキストモードI/Oの誤解です。文字「\n」をテキストモードファイルに書き込むと、OSは「\n」文字を新しい行のプラットフォーム固有の表現に置き換えることができます。ただし、これは'\n'の実際の値が異なることを意味するものではありません。変更は、ファイルを書き込むためのライブラリ内でのみ行われます。
たとえば、ファイルをテキストモードで開き、「\ n」と書き込んでから、ファイルをバイナリモードで開き、書き込まれたデータを「\ n」と比較すると、書き込まれたデータは「\n」とは異なる場合があります。
#include <fstream>
#include <iostream>
int main() {
char const * filename = "test.txt";
{
std::ofstream fout(filename);
fout << '\n';
}
{
std::ifstream fin(filename, std::ios::binary);
char buf[100] = {};
fin.read(buf, sizeof(buf));
if (sizeof('\n') == fin.gcount() && buf[0] == '\n') {
std::cout << "text mode written '\\n' matches value of '\\n'\n";
} else {
// This will be executed on Windows
std::cout << "text mode written '\\n' does not match value of '\\n'\n";
}
}
}
これは、「\n」構文の使用にも依存しません。0xA
上記のASCII改行文字を使用して書き直すことができ、結果はWindowsでも同じになります。(つまり、バイト0xA
をテキストモードファイルに書き込むと、Windowsは実際に2バイトを書き込みます0xD 0xA
。)
文字列リテラルを省略し、文字の16進値を使用するだけで、すべてが正常にコンパイルされることがわかりました。
たとえば、次の行を変更します。
wchar_t c = L'\u0000';
...に:
wchar_t c = 0x0000;
ただし、これが実際にUCNによって提供されるのと同じ独立した値を保持しているかどうかはまだわかりません。