Мне непонятно, какие кодировки используются там, где в C argv
. В частности, меня интересует следующий сценарий:
- Пользователь использует локаль L1 для создания файла с именем
N
, содержит символы, отличные от ASCII - Позже пользователь использует локаль L2 для добавления имени файла в командной строке, который передается в программу P как аргумент командной строки
Какая последовательность байтов P видна в командной строке?
Я заметил, что в Linux, создавая имя файла в локали UTF-8, а затем заворачивая его в (например), локаль zw_TW.big5
, кажется, заставляет мою программу P подавать UTF-8, а не Big5
. Тем не менее, в OS X такая же серия действий приводит к тому, что моя программа P получает кодированное имя Big5
.
Вот что я думаю до сих пор (длинный, и я, вероятно, ошибаюсь и нуждаюсь в исправлении):
Окна
Имена файлов хранятся на диске в формате 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
для изменения вашей локали.