Использование zlib с путями к файлам Unicode в Windows - PullRequest
8 голосов
/ 15 марта 2012

Я читаю сжатые gzip файлы, используя zlib. Затем вы открываете файл, используя

gzFile gzopen(const char *filepath, const char *mode);

Как вы обрабатываете пути к файлам Unicode, которые хранятся как const wchar_t* в Windows?

На UNIX-подобных платформах вы можете просто преобразовать путь к файлу в UTF-8 и вызвать gzopen (), но это не будет работать в Windows.

Ответы [ 5 ]

13 голосов
/ 17 марта 2012

Следующая версия zlib будет включать эту функцию, где _WIN32 равно #defined:

gzFile gzopen_w(const wchar_t *path, char *mode);

Он работает точно так же, как gzopen(), за исключением того, что он использует _wopen() вместо open().

Я специально не дублировал второй аргумент _wfopen(), и в результате я не назвал его _wgzopen(), чтобы избежать возможной путаницы с аргументами этой функции. Отсюда и название gzopen_w(). Это также позволяет избежать использования зарезервированного пространства имен C.

11 голосов
/ 16 марта 2012

Прежде всего, что является именем файла?

В Unix-подобных системах

Имя файла - последовательность байтов , оканчивающаяся нануль.Ядру не нужно заботиться о кодировке символов (кроме как знать код ASCII для /).

Однако с точки зрения пользователей удобнее интерпретировать имена файлов как последовательности символы , и это делается с помощью кодировки символов , указанной как часть языкового стандарта . Юникод поддерживается благодаря обеспечению доступности языковых стандартов UTF-8.

В программах на C файлы представлены обычными char* строками в таких функциях, как fopen. Не существует широкоформатной версии POSIX API. Если у вас есть wchar_t* имя файла, вы должны явно преобразовать его в char*.

В Windows NT

Имя файла последовательность кодовых единиц UTF-16 .Фактически, все манипулирование строками в Windows выполняется внутри UTF-16.

Все библиотеки Microsoft C (++), включая библиотеку времени выполнения Visual C ++, используют соглашение, *Строки 1038 * находятся в устаревшей кодовой странице ANSI для конкретной локали, а строки wchar_t* - в UTF-16.А функции char* являются просто оболочками с обратной совместимостью для новых функций wchar_t*.

Итак, если вы вызываете MessageBoxA(hwnd, text, caption, type), это по сути то же самое, что и вызов MessageBoxW(hwnd, ToUTF16(text), ToUTF16(caption), type),И когда вы вызываете fopen(filename, mode), это похоже на _wfopen(ToUTF16(filename), ToUTF16(mode)).

Обратите внимание, что _wfopen является одной из многих нестандартных функций C для работы со строками wchar_t*.И это не только для удобства; вы не можете использовать стандартные char* эквиваленты , поскольку они ограничивают вас кодовой страницей "ANSI" (которая не может быть UTF-8 ),Например, в локали windows-1252 вы не можете (легко) fopen файл שלום.c, потому что просто невозможно представить эти символы в узкой строке.

В кроссплатформенномбиблиотеки

Вот некоторые типичные подходы:

  1. Используйте стандартные функции C со строками char*, и просто не давайте ? о поддержке не-ANSI символов в Windows.
  2. Используйте char* строки, но интерпретируйте их как UTF-8 вместо ANSI.В Windows напишите функции-оболочки, которые принимают аргументы UTF-8, преобразуют их в UTF-16 и вызывают такие функции, как _wfopen.
  3. . Повсеместно используйте широкие символьные строки, что похоже на №2, за исключением того, что вам нужнонаписать функции-оболочки для не -Windows систем.

Как zlib обрабатывает имена файлов?

К сожалению, похоже, что используется наивный подход # 1 выше, сopen (а не _wopen) используется напрямую.

Как вы можете обойти это?

Помимо уже упомянутых решений (мое любимое из них - предложение gzdopen Appleman1234),Вы можете воспользоваться символьными ссылками , чтобы дать файлу альтернативное полностью ASCII-имя, которое затем можно безопасно передать gzopen.Возможно, вам даже не придется делать это, если файл уже имеет подходящее короткое имя .

4 голосов
/ 16 марта 2012

У вас есть следующие опции

 #ifdef _WIN32 

 #define F_OPEN(name, mode) _wfopen((name), (mode))

 #endif    
  1. Патч zlib, чтобы он использовал _wfopen в Windows вместо fopen, используя что-то похожее на приведенное выше в zutil.h

  2. Используйте _wfopen или _wopen вместо gzopen и передайте возвращаемое значение в gzdopen.

  3. Используйте libiconv или некоторые другиедругая библиотека, чтобы изменить файл, содержащий ASCII, из заданной вами кодировки Unicode, и передать строку ASCII в gzopen.Если libiconv завершается ошибкой, вы обрабатываете ошибку и предлагаете пользователю переименовать файл.

Для получения дополнительной информации о iconv см. Пример iconv .В этом примере используется японский для UTF-8, но не было бы большого скачка, чтобы изменить целевую кодировку на ASCII или ISO 8859-1.

Для получения дополнительной информации о преобразовании символов zlib и не ANSI см. здесь

3 голосов
/ 16 марта 2012

Вот реализация варианта Appleman # 2.Код был протестирован.

#ifdef _WIN32

gzFile _wgzopen(const wchar_t* fileName, const wchar_t* mode)
{
    FILE* stream = NULL;
    gzFile gzstream = NULL;
    char* cmode = NULL;         // mode converted to char*
    int n = -1;

    stream = _wfopen(fileName, mode);

    if(stream)
        n = wcstombs(NULL, mode, 0);
    if(n != -1)
        cmode = (char*)malloc(n + 1);
    if(cmode) {
        wcstombs(cmode, mode, n + 1);
        gzstream = gzdopen(fileno(stream), cmode);
    }

    free(cmode);
    if(stream && !gzstream) fclose(stream);
    return gzstream;
}

#endif

Я сделал оба filename и mode const wchar_t* для совместимости с функциями Windows, такими как

FILE* _wfopen(const wchar_t* filename, const wchar_t* mode);
0 голосов
/ 26 октября 2015

Вот моя собственная версия вспомогательной функции Unicode, протестированная немного лучше, чем версия выше.

static void GetFlags(const char* mode, int& flags, int& pmode)
{
    const char* _mode = mode;

    flags = 0;      // == O_RDONLY
    pmode = 0;      // pmode needs to be obtained, otherwise file gets read-only attribute, see 
                    // /953320/pochemu-atribut-tolko-dlya-chteniya-ustanavlivaetsya-inogda-dlya-failov-sozdannyh-moei-sluzhboi

    for( ; *_mode ; _mode++ )
    {
        switch( tolower(*_mode) )
        {
            case 'w':
                flags |= O_CREAT | O_TRUNC;
                pmode |= _S_IWRITE;
                break;
            case 'a':
                flags |= O_CREAT | O_APPEND;
                pmode |= _S_IREAD | _S_IWRITE;
                break;
            case 'r':
                pmode |= _S_IREAD;
                break;
            case 'b':
                flags |= O_BINARY;
                break;
            case '+':
                flags |= O_RDWR;
                pmode |= _S_IREAD | _S_IWRITE;
                break;
        }
    }

    if( (flags & O_CREAT) != 0 && (flags & O_RDWR) == 0 )
        flags |= O_WRONLY;
} //GetFlags


gzFile wgzopen(const wchar_t* fileName, const char* mode)
{
    gzFile gzstream = NULL;
    int f = 0;
    int flags = 0;
    int pmode = 0;

    GetFlags(mode, flags, pmode);

    f = _wopen(fileName, flags, pmode );

    if( f == -1 )
        return NULL;

    // gzdopen will also close file handle.
    gzstream = gzdopen(f, mode);
    if(!gzstream)
        _close(f);
    return gzstream;
}
...