Мне не ясно, какие кодировки используются где в C argv
.В частности, меня интересует следующий сценарий:
- Пользователь использует языковой стандарт L1 для создания файла, имя которого
N
содержит символы не ASCII - Позжепользователь использует языковой стандарт L2, чтобы завершить табуляции имя этого файла в командной строке, которое вводится в программу P в качестве аргумента командной строки
Какую последовательность байтов P видит вкомандная строка?
Я заметил, что в Linux создание имени файла в локали UTF-8 и последующее его завершение (например, в локали zw_TW.big5
) приводит к тому, что моя программа P получает питание UTF-8 вместо Big5
.Однако в OS X та же серия действий приводит к тому, что моя программа P получает закодированное Big5
имя файла.
Вот что я думаю, что происходит до сих пор (долго, и я, вероятно, ошибаюсь и нуждаюсьисправляется):
Windows
Имена файлов хранятся на диске в некотором формате Unicode.Поэтому Windows принимает имя N
, преобразует из L1 (текущей кодовой страницы) в версию Unicode N
, которую мы будем называть N1
, и сохраняет N1
на диске.
Что я тогда Предположим, случается, что при последующем заполнении табуляции имя N1
преобразуется в язык L2 (новую текущую кодовую страницу) для отображения.Если повезет, это даст исходное имя N
- но это не будет правдой, если N
содержит символы, непредставимые в L2.Мы называем новое имя N2
.
Когда пользователь фактически нажимает Enter для запуска P с этим аргументом, имя N2
преобразуется обратно в Unicode, снова получая N1
.Этот N1
теперь доступен программе в формате UCS2 через GetCommandLineW
/ wmain
/ tmain
, но пользователи GetCommandLine
/ main
увидят имя N2
в текущей локали (кодовая страница).
OS X
Насколько я знаю, история хранения на диске такая же.OS X хранит имена файлов как Unicode.
С терминалом Unicode я думаю , что происходит, когда терминал создает командную строку в буфере Unicode.Поэтому, когда вы завершите вкладку, он копирует имя файла как имя файла Unicode в этот буфер.
Когда вы запускаете команду, этот буфер Unicode конвертируется в текущий языковой стандарт L2 и подается в программу черезargv
, и программа может декодировать argv с текущей локалью в Unicode для отображения.
Linux
В Linux все по-другому, и я очень озадачен тем, что происходит,Linux хранит имена файлов в виде строк байтов , а не в Unicode.Таким образом, если вы создаете файл с именем N
в локали L1, то N
в виде строки байтов - это то, что хранится на диске.
Когда я позже запусту терминал и попытаюсь завершить имя табуляцией,Я не уверен, что происходит.Мне кажется, что командная строка построена в виде байтового буфера, а имя файла в виде байтовой строки просто конкатенируется в этот буфер.Я предполагаю, что когда вы набираете стандартный символ, он на лету кодируется в байты, которые добавляются в этот буфер.
Когда вы запускаете программу, я думаю, что буфер отправляется непосредственно в argv
.Теперь, какая кодировка argv
имеет?Похоже, что любые символы, введенные вами в командной строке в локали L2, будут в кодировке L2, но имя файла будет в кодировке L1 .Итак, argv
содержит смесь двух кодировок!
Вопрос
Мне бы очень понравилось, если бы кто-то мог сообщить мне, что здесь происходит.Все, что у меня есть на данный момент, - это полугодия и предположения, и они не совсем подходят друг другу.То, что я действительно хотел бы быть правдой, это чтобы кодировку argv
кодировали в текущей кодовой странице (Windows) или в текущей локали (Linux / OS X), но это не так ...
Дополнительно
Вот простая программа-кандидат P, которая позволяет вам наблюдать за кодировками для себя:
#include <stdio.h>
int main(int argc, char **argv)
{
if (argc < 2) {
printf("Not enough arguments\n");
return 1;
}
int len = 0;
for (char *c = argv[1]; *c; c++, len++) {
printf("%d ", (int)(*c));
}
printf("\nLength: %d\n", len);
return 0;
}
Вы можете использовать locale -a
, чтобы увидеть доступные локали, и использовать export LC_ALL=my_encoding
сменить язык.