Я пишу Lexer в MSVC, и мне нужен способ представить точный совпадение символов для всех 128 Базовый латинский Unicode персонажи.
Однако согласно эта статья MSDN, «За исключением 0x24 и 0x40, символы в диапазоне от 0 до 0x20 и от 0x7f до 0x9f не могут быть представлены с универсальным именем символа (UCN)».
…Что в основном означает, что я не могу объявить что-то вроде wchar_t c = '\u0000';
не говоря уже о том, чтобы использовать оператор switch в этом «запрещенном» диапазоне символов. Кроме того, для ‘\ n’ и ‘\ r’, насколько я понимаю, фактические значения / длины варьируются между компиляторами / целевыми ОС …
(то есть Windows использует ‘\ r \ n’, в то время как Unix просто использует ‘\ n’, а более старые версии MacOS используют ‘\ r’)
…и поэтому я сделал обходной путь для этого, используя универсальные символы, чтобы гарантировать, что надлежащие схемы кодирования и длины байтов обнаружены и используются должным образом.
Но эта ошибка компилятора C3850 просто не позволяет мне делать все по-своему …
Так как это можно решить таким образом, чтобы гарантирует правильные схемы кодирования & совпадения персонажей учитывая любой источник ввода?
В C ++ 11 ограничения на то, какие символы вы можете представлять с помощью универсальных имен символов, не применяются внутри символьных и строковых литералов.
C ++ 11 2.3 / 2
Кроме того, если шестнадцатеричное значение для универсального имени символа вне с-символ последовательности, S-символ последовательности, или же г-символ последовательности символа или строкового литерала соответствует управляющему символу (в любом из диапазонов 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 и соберете его с помощью компилятора Microsoft в системе Windows, настроенной на использование кодовой страницы 1252, то компилятор преобразует представление «µ» в UTF-16 в представление CP1252. Если вы создадите этот источник в системе, настроенной с другой кодовой страницей, которая не содержит символа, то компилятор выдаст предупреждение / ошибку, если ему не удастся преобразовать символ в эту кодовую страницу.
Точно так же, если вы сохраните исходный код как UTF-8 (с так называемой «спецификацией», чтобы компилятор знал, что кодировка UTF-8), то он преобразует исходное представление символа UTF-8 символа в систему. кодовая страница, если это возможно, что бы это ни было.
И если вы замените ‘µ’ на UCN, ‘\ u00B5’, компилятор все равно будет делать то же самое; если возможно, он преобразует UCN в представление кодовой страницы системы U + 00B5 MICRO SIGN.
Так как же это можно решить таким образом, чтобы обеспечить правильные схемы кодирования & совпадения символов при ЛЮБОМ исходном вводе?
Я не уверен, что вы спрашиваете. Я предполагаю, что вы хотите убедиться, что интегральные значения char
или же wchar_t
переменные / литералы согласуются с определенной схемой кодирования (вероятно, ASCII, поскольку вы спрашиваете только о символах в диапазоне ASCII), но что такое «исходный ввод»? Кодировка исходных файлов вашего лексера? Кодировка ввода для вашего лексера? Как вы ожидаете, что «исходные данные» будут меняться?
Кроме того, для ‘\ n’ и ‘\ r’, насколько я понимаю, фактические значения / длины варьируются между компиляторами / целевыми ОС …
(то есть Windows использует ‘\ r \ n’, в то время как Unix просто использует ‘\ n’, а более старые версии MacOS используют ‘\ r’)
Это недоразумение ввода-вывода в текстовом режиме. Когда вы записываете символ ‘\ n’ в файл текстового режима, ОС может заменить символ ‘\ 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 фактически запишет два байта 0xD 0xA
.)
Я обнаружил, что опуская строковый литерал и просто используя шестнадцатеричное значение символа, можно легко скомпилировать все.
Например, вы бы изменили следующую строку:
wchar_t c = L'\u0000';
…чтобы:
wchar_t c = 0x0000;
Хотя, я все еще не уверен, содержит ли это те же самые независимые значения, которые предоставляет UCN.