sscanf_s не возвращает первый символ строки - PullRequest
1 голос
/ 18 февраля 2020

Я пытаюсь найти первую строку (не более 4 символов) в списке строк, разделенных запятыми, внутри C char-array.

Я пытаюсь добиться этого с помощью sscanf_s (под Windows) и строка управления форматом %[^,]:

char mystring[] = "STR1,STR2";
char temp[5];

if (sscanf_s(mystring, "%[^,]", temp, 5) != 0) {
    if (strcmp(temp, "STR1") == 0) { return 0; }
    else if (strcmp(temp, "STR2") == 0) { return 1; }
    else { return -1; }
}

После вызова sscanf_s содержимое temp не STR1, а \0TR1 (\0 в ASCII-интерпретация 0). И возвращается значение -1.

Почему я получаю такое поведение и как мне исправить мой код, чтобы получить правильный результат (возврат 0)?

РЕДАКТИРОВАТЬ: изменено char mystring до mystring[] (я должен был убедиться, что набрал его правильно)

Ответы [ 2 ]

2 голосов
/ 19 февраля 2020

В вашем коде несколько проблем:

  • mystring определяется как char, а не как указатель строки.
  • аргумент 5 после temp в sscanf_s() должен иметь тип rsize_t, который совпадает с size_t. Вы должны указать его как sizeof(temp).
  • . Вы должны указать максимальное количество символов для хранения в массиве назначения в строке формата, чтобы избежать неинтеллектуального поведения sscanf_s в случае переполнения.
  • sscanf_s возвращает 1, если он может преобразовать и сохранить строку. Тестирование != 0 также примет EOF, что является ошибкой ввода, для которой содержимое temp является неопределенным.

Вот измененная версия:

const char *mystring = "STR1,STR2";
char temp[5];

if (sscanf_s(mystring, "%4[^,]", temp, sizeof temp) == 1) {
    if (strcmp(temp, "STR1") == 0) {
        return 0;
    } else
    if (strcmp(temp, "STR2") == 0) {
        return 1; 
    } else {
        return -1;
    }
}

ОБНОВЛЕНИЕ: ОП использует Microsoft Visual Studio, которая, кажется, имеет несоответствующую реализацию так называемых потоковых функций secure . Вот цитата из их страницы документации :

Функция sscanf_s считывает данные из буфера в местоположение, заданное каждым аргументом. Аргументы после строки формата указывают на переменные, тип которых соответствует спецификатору типа в формате. В отличие от менее защищенной версии sscanf, параметр размера буфера требуется при использовании символов поля типа c, C, s, S или наборов управления строкой, которые заключены в []. Размер буфера в символах должен указываться как дополнительный параметр сразу после каждого параметра буфера, который требует его. Например, если вы читаете в строку, размер буфера для этой строки передается следующим образом:

wchar_t ws[10];
swscanf_s(in_str, L"%9s", ws, (unsigned)_countof(ws)); // buffer size is 10, width specification is 9

Размер буфера включает завершающий ноль. Поле спецификации ширины может использоваться, чтобы гарантировать, что считываемый токен будет помещаться в буфер. Если поле спецификации ширины не используется, и считываемый токен слишком велик для размещения в буфере, в этот буфер ничего не записывается.

В случае символов один символ может быть прочитан следующим образом:

wchar_t wc;
swscanf_s(in_str, L"%c", &wc, 1);

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

char c[4];
sscanf_s(input, "%4c", &c, (unsigned)_countof(c)); // not null terminated

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

char c[4];
sscanf_s(input, "%4c", &c, (unsigned)_countof(c)); // not null terminated

Эта спецификация несовместима со стандартом C, который определяет тип аргументов ширины rsize_t и тип rsize_t того же типа, что и size_t.

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

2 голосов
/ 19 февраля 2020

отредактировано согласно комментарию из chqrl ie

относительно:

if(sscanf_s(mystring, "%[^,]",temp, 5) != 0){

Спецификатор преобразования формата ввода: %[..] всегда добавляет байт NUL в конец ввода. Таким образом, спецификатор преобразования входного формата должен быть: "% 4 [^,]" Результат после исправления:

if(sscanf_s(mystring, "%4[^,]",temp, 5) != 0){

также, независимо от того, сколько раз был выполнен этот фрагмент кода, возвращаемое значение wnce другие проблемы будут исправлены ВСЕГДА будет STR1

относительно утверждения;

char mystring = "STR1,STR2";

Это недопустимое утверждение. Предложить:

char *mystring = "STR1,STR2";  // notice the '*'

- или -

char mystring[] = "STR1,STR2";  // notice the '[]'
...