Можно ли использовать Unicode «argv»?
-
12-09-2019 - |
Вопрос
Я пишу небольшую оболочку для приложения, которое использует файлы в качестве аргументов.
Оболочка должна быть в Юникоде, поэтому я использую wchar_t для имеющихся у меня символов и строк.Теперь я столкнулся с проблемой: мне нужно, чтобы аргументы программы были в массиве wchar_t и в строке wchar_t.
Является ли это возможным?Я определяю main
функционировать как
int main(int argc, char *argv[])
Должен ли я использовать wchar_t для argv
?
Большое спасибо, похоже, я не нашел полезной информации о том, как правильно использовать Unicode в C.
Решение
В общем, нет.Это будет зависеть от операционной системы, но стандарт C гласит, что аргументы main() должны быть main(int argc, char **argv) или эквивалентные, поэтому, если char и wchar_t не являются одним и тем же базовым типом , ты не можешь этого сделать.
При этом вы можете добавить в программу строки аргументов UTF-8, преобразовать их в UTF-16 или UTF-32, а затем продолжать жить дальше.
На Mac (10.5.8, Leopard) я получил:
Osiris JL: echo "ï€" | odx
0x0000: C3 AF E2 82 AC 0A ......
0x0006:
Osiris JL:
Это все в кодировке UTF-8.(odx — это программа для создания шестнадцатеричного дампа).
Смотрите также: Почему при взаимодействии со средой UNIX/Linux используется кодировка UTF-8?
Другие советы
Портативный код его не поддерживает.Windows (например) поддерживает использование wmain
вместо main
, и в этом случае argv передается как широкие символы.
В Windows вы можете использовать GetCommandLineW()
и CommandLineToArgvW()
создать стиль argv wchar_t[]
массив, даже если приложение не скомпилировано для Unicode.
В любом случае в Windows вы можете иметь wmain()
для сборок UNICODE.Хоть и не портативный.Я не знаю, предоставляют ли платформы GCC или Unix/Linux что-то подобное.
Предполагая, что ваша среда Linux использует кодировку UTF-8, следующий код подготовит вашу программу для простой обработки Unicode на C++:
int main(int argc, char * argv[]) {
std::setlocale(LC_CTYPE, "");
// ...
}
Далее, тип wchar_t в Linux является 32-битным, что означает, что он может хранить отдельные кодовые точки Юникода, и вы можете безопасно использовать тип wstring для классической обработки строк в C++ (посимвольно).С помощью вызова setlocale, описанного выше, вставка в wcout автоматически преобразует ваш вывод в UTF-8, а извлечение из wcin автоматически преобразует ввод UTF-8 в UTF-32 (1 символ = 1 кодовая точка).Единственная проблема, которая остается, заключается в том, что строки argv[i] по-прежнему имеют кодировку UTF-8.
Вы можете использовать следующую функцию для декодирования UTF-8 в UTF-32.Если входная строка повреждена, она будет возвращать правильно преобразованные символы до тех пор, пока не будут нарушены правила UTF-8.Вы можете улучшить его, если вам нужно больше отчетов об ошибках.Но для данных argv можно смело предположить, что это правильный UTF-8:
#define ARR_LEN(x) (sizeof(x)/sizeof(x[0]))
wstring Convert(const char * s) {
typedef unsigned char byte;
struct Level {
byte Head, Data, Null;
Level(byte h, byte d) {
Head = h; // the head shifted to the right
Data = d; // number of data bits
Null = h << d; // encoded byte with zero data bits
}
bool encoded(byte b) { return b>>Data == Head; }
}; // struct Level
Level lev[] = {
Level(2, 6),
Level(6, 5),
Level(14, 4),
Level(30, 3),
Level(62, 2),
Level(126, 1)
};
wchar_t wc = 0;
const char * p = s;
wstring result;
while (*p != 0) {
byte b = *p++;
if (b>>7 == 0) { // deal with ASCII
wc = b;
result.push_back(wc);
continue;
} // ASCII
bool found = false;
for (int i = 1; i < ARR_LEN(lev); ++i) {
if (lev[i].encoded(b)) {
wc = b ^ lev[i].Null; // remove the head
wc <<= lev[0].Data * i;
for (int j = i; j > 0; --j) { // trailing bytes
if (*p == 0) return result; // unexpected
b = *p++;
if (!lev[0].encoded(b)) // encoding corrupted
return result;
wchar_t tmp = b ^ lev[0].Null;
wc |= tmp << lev[0].Data*(j-1);
} // trailing bytes
result.push_back(wc);
found = true;
break;
} // lev[i]
} // for lev
if (!found) return result; // encoding incorrect
} // while
return result;
} // wstring Convert
В Windows вы можете использовать tchar.h и _tmain, которые будут преобразованы в wmain, если символ _UNICODE определен во время компиляции, или в main в противном случае.TCHAR *argv[] аналогичным образом будет расширен до WCHAR * argv[], если определен unicode, и char * argv[] в противном случае.
Если вы хотите, чтобы ваш основной метод работал кроссплатформенно, вы можете определить свои собственные макросы с тем же эффектом.
TCHAR.h содержит ряд удобных макросов для преобразования между wchar и char.