Какова цель случая s == NULL для mbrtowc? - PullRequest
6 голосов
/ 17 января 2011

mbrtowc указано для обработки указателя NULL для аргумента s (многобайтовый символьный указатель) следующим образом:

Если s является нулевым указателем, функция mbrtowc ()должен быть эквивалентен вызову:

mbrtowc(NULL, "", 1, ps)

В этом случае значения аргументов pwc и n игнорируются.

Насколько я могу судить, это использованиев основном бесполезно.Если ps не хранит какой-либо частично преобразованный символ, вызов просто вернет 0 без побочных эффектов.Если ps хранит частично преобразованный символ, то, поскольку '\0' недопустим в качестве следующего байта в многобайтовой последовательности ('\0' может быть только символом конца строки), вызов вернет (size_t)-1 с errno==EILSEQ.и оставьте ps в неопределенном состоянии.

Предполагается, что предполагалось использовать для сброса переменную состояния, особенно когда NULL передается для ps и используется внутреннее состояние, аналогичноеПоведение mbtowc с кодировками с сохранением состояния, но это нигде не указано , насколько я могу судить, и оно конфликтует с семантикой для хранения mbrtowc частично преобразованных символов (если mbrtowc должны были сбросить состояние при обнаружении байта 0 после потенциально допустимой начальной подпоследовательности, он не смог бы обнаружить эту опасную недопустимую последовательность).

Если было указано mbrtowc для сброса переменной состояния только тогда, когдаs равен NULL, но не тогда, когда он указывает на 0 байт, желательное поведение сброса состояния было бы возможным, но такое поведение нарушало бы стандарт, как написано.Это дефект в стандарте?Насколько я могу судить, нет абсолютно никакого способа сбросить внутреннее состояние (используется, когда ps равно NULL) после того, как обнаружена недопустимая последовательность, и, следовательно, никакая правильная программа не может использовать mbrtowc с ps==NULL.

Ответы [ 2 ]

5 голосов
/ 17 января 2011

Поскольку байт '\ 0' должен преобразовываться в широкий нулевой символ независимо от состояния сдвига (5.2.1.2 многобайтовых символов), а функция mbrtowc() указана для сброса состояния сдвига при преобразовании в широкий нулевой символ (7.24.6.3.2 / 3 функция mbrtowc), вызов mbrtowc( NULL, "", 1, ps) сбросит состояние сдвига, сохраненное в mbstate_t, указанном ps. И если mbrtowc( NULL, "", 1, NULL) вызывается для использования внутреннего объекта mbstate_t библиотеки, он будет сброшен в исходное состояние. В конце ответа приводятся ссылки на соответствующие биты стандарта.

Я ни в коем случае не особенно разбираюсь в стандартных многобайтовых функциях преобразования C (мой опыт с такими вещами заключался в использовании Win32 API для преобразования).

Если mbrtowc() обрабатывает «неполный символ», который обрезается на 0 байтов, он должен возвратить (size_t)(-1), чтобы указать недопустимый многобайтовый символ (и, таким образом, обнаружить опасную ситуацию, которую вы описываете). В этом случае состояние преобразования / сдвига не определено (и я думаю, что вы в основном используете эту строку). Многобайтовая «последовательность», в которой была предпринята попытка преобразования, но содержащая '\0', недействительна и будет действительна для последующих данных. Если '\0 'не должен был быть частью преобразованной последовательности, то он не должен был быть включен в число байтов, доступных для обработки.

Если вы находитесь в ситуации, когда вы можете получить дополнительные, последующие байты для частичного многобайтового символа (скажем, из сетевого потока), n, который вы передали для частичного многобайтового символа, не должен включать 0 байт, так что вы получите (size_t)(-2) возвращено. В этом случае, если вы передадите '\0' в середине частичного преобразования, вы потеряете тот факт, что произошла ошибка, и в качестве побочного эффекта сбросьте используемое состояние mbstate_t (независимо от того, ваше это или внутренний используется потому, что вы передали NULL-указатель для ps). Я думаю, что я в сущности повторяю ваш вопрос здесь.

Однако я думаю, что можно обнаружить и обработать эту ситуацию, но, к сожалению, она требует отслеживания какого-то состояния самостоятельно:

#define MB_ERROR    ((size_t)(-1))
#define MB_PARTIAL  ((size_t)(-2))

// function to get a stream of multibyte characters from somewhere
int get_next(void);

int bar(void)
{
    char c;
    wchar_t wc;
    mbstate_t state = {0};

    int in_partial_convert = 0;

    while ((c = get_next()) != EOF)
    {
        size_t result = mbrtowc( &wc, &c, 1, &state);

        switch (result) {
        case MB_ERROR:
            // this multibyte char is invalid
            return -1;
        case MB_PARTIAL:
            // do nothing yet, we need more data
            // but remember that we're in this state
            in_partial_convert = 1;
            break;
        case 1:
            // output the competed wide char
            in_partial_convert = 0;     // no longer in the middle of a conversion
            putwchar(wc);
            break;
        case 0:
            if (in_partial_convert) {
                // this 'last' multibyte char was mal-formed
                // return an error condidtion
                return -1;
            }
            // end of the multibyte string
            // we'll handle similar to EOF
            return 0;
        }
    }

    return 0;
}

Может быть, не идеальная ситуация, но я думаю, что это показывает, что она не полностью сломана, поэтому ее невозможно использовать.


Стандарты цитирования:

5.2.1.2 Многобайтовые символы

  • Многобайтовый набор символов может иметь зависящее от состояния кодирование, в котором каждая последовательность многобайтовых символов начинается в начальном состоянии сдвига и входит в другой языковой сдвиг состояния, когда конкретный многобайтовый символы встречаются в последовательность. В то время как в начальной смене состояние, все однобайтовые символы сохранить их обычную интерпретацию и не меняйте состояние сдвига. интерпретация для последующих байтов в последовательность является функцией текущее состояние сдвига.

  • Байт со всеми нулевыми битами должен интерпретироваться как нулевой символ не зависит от состояния сдвига.

  • Байт со всеми нулевыми битами не должен появляться во втором или последующих байты многобайтового символа.

7.24.6.3.2 / 3 Функция mbrtowc

Если соответствующий широкий символ нулевой широкий символ, результирующий описанное состояние является начальным состояние преобразования

1 голос
/ 26 января 2011

В 5.2.1.2, Многобайтовые символы, Стандарт C гласит:

Байт со всеми нулевыми битами должен интерпретироваться как нулевой символ независимо от сдвига государство. Такой байт не должен встречаться как часть любого другого многобайтового символа.

Стандарт, по-видимому, проводит различие между состоянием сдвига и состоянием преобразования, как, например, в 7.24.6 упоминается:

Состояние преобразования, описываемое указанным объектом, изменяется по мере необходимости для отслеживания состояния сдвига и положения в многобайтовом символе для связанной последовательности многобайтовых символов.

(акцент добавлен мной). Тем не менее, я думаю, что цель состоит в том, чтобы интерпретировать байт со всеми нулевыми битами как нулевой символ независимо от значения mbstate_t, которое кодирует все состояние преобразования, в частности как «Такой байт не должен встречаться как часть любого другого многобайтового символа» подразумевает, что нулевой байт не может встречаться в многобайтовом символе. Если нулевой байт действительно возникает при ошибочном вводе, где должен быть второй, третий и т. Д. Байт многобайтового символа, то я интерпретирую Стандарт как высказывание, что частичный многобайтовый символ в EOF молча игнорируется.

Мое чтение 7.24.6.3.2, функция mbrtowc, для случая, когда s равен NULL, таким образом: следующий 1 байт завершает нулевой широкий символ, возвращаемое значение mbrtowc равно 0, а результирующее состояние является исходным состоянием преобразования, потому что:

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

Передав NULL для s и ps, внутренний mbstate_t mbrtowc сбрасывается в исходное состояние.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...