В какой кодировке хранятся символьные / строковые литералы?(Или как найти буквальный символ в строке из ввода?) - PullRequest
0 голосов
/ 24 ноября 2018

Как мы знаем, разные кодировки отображают разные представления одним и тем же символам.Используя setlocale, мы можем указать кодировку строк, которые считываются из ввода, но относится ли это и к строковым литералам?Я нахожу это удивительным, поскольку это время компиляции!

Это важно для таких простых задач, как, например, определение, содержит ли строка, читаемая из ввода, определенный символ.При чтении строк из ввода кажется разумным установить языковой стандарт в соответствии с языковым стандартом пользователя (setlocale("LC_ALL", "");), чтобы строка была правильно прочитана и обработана.Но когда мы сравниваем эту строку с символьным литералом, не возникнут ли проблемы из-за несовпадающей кодировки?

Другими словами: мне кажется, что следующий фрагмент работает.Но разве это не работает только из-за совпадения?Потому что - например?- исходный код был сохранен в той же кодировке, которая использовалась на компьютере во время выполнения?

#include <stdio.h>
#include <wchar.h>
#include <stdlib.h>
#include <locale.h>

int main()
{
        setlocale(LC_ALL, "");

        // Read line and convert it to wide string so that wcschr can be used
        // So many lines! And that's even though I'm omitting the necessary
        // error checking for brevity. Ah I'm also omitting free's
        char *s = NULL; size_t n = 0;
        getline(&s, &n, stdin);
        mbstate_t st = {0}; const char* cs = s;
        size_t wn = mbsrtowcs(NULL, &cs, 0, &st);
        wchar_t *ws = malloc((wn+1) * sizeof(wchar_t));
        st = (mbstate_t){0};
        mbsrtowcs(ws, &cs, (wn+1), &st);

        int contains_guitar = (wcschr(ws, L'?') != NULL);
        if(contains_guitar)
                printf("Let's rock!\n");
        else
                printf("Let's not.\n");
        return 0;
}

Как это сделать правильно?

Ответы [ 2 ]

0 голосов
/ 24 ноября 2018

Если вы готовы принять UTF-8,

strstr(s,"?")

или:

strstr(s,u8"?")

Последний избегает некоторых предположений, но требует компилятора C11.Если вы хотите лучшее из обоих и можете пожертвовать удобочитаемостью:

strstr(s,"\360\237\216\270")
0 голосов
/ 24 ноября 2018

Используя setlocale, мы можем указать кодировку строк, которые читаются из входных данных, но относится ли это и к строковым литералам?

Нет.Строковые литералы используют набор символов выполнения , который определяется вашим компилятором во время компиляции.

Набор символов выполнения не обязательно должен совпадать с исходный набор символов , набор символов, используемый в исходном коде.Компилятор C отвечает за перевод и должен иметь параметры для выбора / определения их.Значение по умолчанию зависит от компилятора, но в Linux и в большинстве современных систем POSIXy обычно используется UTF-8.

Следующий фрагмент кода мне подходит.Но разве это не работает только из-за совпадения?

Пример работает, потому что все наборы символов вашей локали, исходный набор символов и набор символов выполнения, использованные при создании двоичного файла, все происходятбыть UTF-8.

Как это сделать правильно?

Два варианта.Одним из них является использование широких символов и строковых литералов.Другой - использовать UTF-8 везде .

Для широкого ввода и вывода, см., Например, этот пример в другом ответе здесь.

Doобратите внимание, что getwline() и getwdelim() не в POSIX.1, а в C11 Приложении K. Это означает, что они являются необязательными, и на момент написания этой статьи вообще не были широко доступны.Таким образом, вместо этого рекомендуется пользовательская реализация около fgetwc().(Один из них, основанный на fgetws(), wcslen() и / или wcscspn(), не сможет правильно обрабатывать встроенные нули, L'\0'.)

В типичной программе с широким вводом / выводом вам нужно только mbstowcs() для преобразования аргументов командной строки и переменных среды в широкие строки.

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

Ваша программа может даже использовать, например,

    if (!setlocale(LC_ALL, ""))
        fprintf(stderr, "Warning: Your C library does not support your current locale.\n");
    if (strcmp("UTF-8", nl_langinfo(CODESET)))
        fprintf(stderr, "Warning: Your locale does not use the UTF-8 character set.\n");

, чтобы проверить, что текущий языковой стандарт использует UTF-8.

У меня естьИспользуются оба подхода в зависимости от обстоятельств.Трудно сказать, какой из них является более переносимым на практике, потому что, как обычно, оба прекрасно работают на не-Windows ОС без проблем.

...