многостроковое сканирование sscanf - PullRequest
0 голосов
/ 19 декабря 2018

Я хочу перехватить следующую строку в c с помощью sscanf

"1=Salam Khobi|FC93F8A120F491F3A8=Rial|F191FEA4"

, но sscanf заполняет только &customInput.type и customInputTitle[0] с помощью "Salam Khobi", а другая часть строки не будет сканироваться.

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

typedef enum {
    INPUT_NUMBER = 0,
    INPUT_NORMAL = 1,
    INPUT_PASSWORD = 2,
    INPUT_PAYAMOUNT = 3,
} inputType;

typedef struct {
    char * title[2];
    char * extra[2];
    inputType type;
    unsigned minLen:6;
    unsigned maxLen:6;
    unsigned forceLen:1;
    unsigned editable:1;
    unsigned char data[100];
} lcdInput;
#define CUSTOM_INPUT_LENGTH     40
static unsigned char customInputTitle[2][CUSTOM_INPUT_LENGTH];
static unsigned char customInputExtra[2][CUSTOM_INPUT_LENGTH];
const char * payload = "1=Salam Khobi|FC93F8A120F491F3A8=Rial|F191FEA4";
#define CUSTOM_INPUT_REGX       "%d=%[^|]s|%[^=]s=%[^|]s|%s"
static lcdInput customInput = {
        .title = {&customInputTitle[0], &customInputTitle[1]},
        .extra = {&customInputExtra[0], &customInputExtra[1]},
        .type = INPUT_NORMAL,
        .editable = 1,
        .forceLen = 0,
};

int main()
{
    memset(&customInputTitle, 0, CUSTOM_INPUT_LENGTH << 1);
    memset(&customInputExtra, 0, CUSTOM_INPUT_LENGTH << 1);

    sscanf(payload, CUSTOM_INPUT_REGX,
           &customInput.type,
           &customInputTitle[0], &customInputTitle[1],
           &customInputExtra[0], &customInputExtra[1]);

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Проблема, о которой спрашивают о

После того, как символ | обнаружен с директивой scanset %[^|], sscanf() возобновит совпадение с символом |.Следующая директива должна быть литералом |, чтобы избежать совпадения.В исходном коде с %[^|]s s является , а не частью директивы scanset, и вместо этого sscanf() ищет совпадение с литералом s на входе.Кроме того, обратите внимание, что спецификаторы максимальной ширины всегда должны использоваться с директивами семейства %s и %[] fscanf(), чтобы избежать переполнения буфера с помощью злонамеренных или искаженных вводов:

"%d=%39[^|]|%39[^=]=%39[^|]|%39s"

Некоторые другие серьезные проблемы

Всегда включать предупреждения при компиляции кода C;это помогло бы вам избежать нескольких серьезных проблем.Для этого кода есть много предупреждений, и большинство проблем, перечисленных ниже, приводит к неопределенному поведению.Я всегда использую по крайней мере gcc -std=c11 -Wall -Wextra -Wpedantic, и я добавил пример вывода gcc для исходного кода в конце ответа.

В опубликованном коде отсутствует #include <string.h> для memset().

Поля .title и .extra в lcdInput должны быть unsigned char *, поскольку они указывают на первые элементы unsigned char массивов.

При инициализации customInput& операторы должны быть удалены.customInput.title и customInput.extra ожидают указателей на unsigned char (или char с до вышеуказанной коррекции).Например, с &customInputTitle[0] у вас есть указатель на массив CUSTOM_INPUT_LENGTH unsigned char с (или char с до вышеуказанной коррекции);это несоответствие типов, и ваш компилятор должен громко жаловаться (с включенными предупреждениями).Вместо этого просто используйте:

static lcdInput customInput = {
    .title = {customInputTitle[0], customInputTitle[1]},
    .extra = {customInputExtra[0], customInputExtra[1]},
    .type = INPUT_NORMAL,
    .editable = 1,
    .forceLen = 0,
};

Здесь customInputTitle[0] - это массив CUSTOM_INPUT_LENGTH unsigned char s, который будет уменьшаться до указателя на его первый элемент (unsigned char *).В качестве альтернативы вы можете использовать &customInputTitle[0][0], &customInputTitle[1][0] и т. Д.

Аналогично вам необходимо удалить амперсанды из массивов customInput в вызове на sscanf().Здесь вам также нужно что-то сделать с &customInput.type.Это тип enum, и вы не можете ввести значение enum.Опять же, компилятор жалуется с включенными предупреждениями.Вместо этого попробуйте:

int typeInput;
if (sscanf(payload, CUSTOM_INPUT_REGX,
           &typeInput,
           customInputTitle[0], customInputTitle[1],
           customInputExtra[0], customInputExtra[1]) == 5) {
    if (typeInput >= INPUT_NUMBER && typeInput <= INPUT_PAYAMOUNT) {
        customInput.type = typeInput;
    } else {
        /* Handle error */
    }
};

Здесь typeInput используется для сбора ввода, значение, возвращаемое sscanf(), проверяется, чтобы убедиться, что было назначено правильное количество значений, и значение typeInputпроверяется по диапазону значений для inputType.Если ввод соответствует ожидаемому, typeInput назначается на customInput.type.

Вызовы на memset() будут работать, но зачем запутывать вещи битовыми сдвигами?Здесь вам не нужны операторы &, но в этом случае они в порядке.Вместо этого рассмотрим:

memset(customInputTitle, 0, sizeof customInputTitle);
memset(customInputExtra, 0, sizeof customInputExtra);

Вот исправленный код.Компилируется без предупреждений, используя gcc -std=c11 -Wall -Wextra -Wpedantic:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>        // missing header

typedef enum {
    INPUT_NUMBER = 0,
    INPUT_NORMAL = 1,
    INPUT_PASSWORD = 2,
    INPUT_PAYAMOUNT = 3,
} inputType;

typedef struct {
    unsigned char * title[2];    // need unsigned char
    unsigned char * extra[2];
    inputType type;
    unsigned minLen:6;
    unsigned maxLen:6;
    unsigned forceLen:1;
    unsigned editable:1;
    unsigned char data[100];
} lcdInput;

#define CUSTOM_INPUT_LENGTH     40

static unsigned char customInputTitle[2][CUSTOM_INPUT_LENGTH];
static unsigned char customInputExtra[2][CUSTOM_INPUT_LENGTH];
const char * payload = "1=Salam Khobi|FC93F8A120F491F3A8=Rial|F191FEA4";

// bad format string
#define CUSTOM_INPUT_REGX       "%d=%39[^|]|%39[^=]=%39[^|]|%39s"

// & operator not needed
static lcdInput customInput = {
    .title = {customInputTitle[0], customInputTitle[1]},
    .extra = {customInputExtra[0], customInputExtra[1]},
    .type = INPUT_NORMAL,
    .editable = 1,
    .forceLen = 0,
};

int main(void)
{
    // could use improvements
    memset(customInputTitle, 0, sizeof customInputTitle);
    memset(customInputExtra, 0, sizeof customInputExtra);

    // & operators not needed
    int typeInput;
    if (sscanf(payload, CUSTOM_INPUT_REGX,
               &typeInput,
               customInputTitle[0], customInputTitle[1],
               customInputExtra[0], customInputExtra[1]) == 5) {
        if (typeInput >= INPUT_NUMBER && typeInput <= INPUT_PAYAMOUNT) {
            customInput.type = typeInput;
        } else {
            /* Handle error */
        }
    };

    return 0;
}

Вывод GCC с предупреждениями

Вот предупреждения компилятора с gcc -std=c11 -Wall -Wextra -Wpedantic из исходной программы, опубликованной в вопросе:

bad_program.c:27:19: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
         .title = {&customInputTitle[0], &customInputTitle[1]},
                   ^
bad_program.c:27:19: note: (near initialization for ‘customInput.title[0]’)
bad_program.c:27:41: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
         .title = {&customInputTitle[0], &customInputTitle[1]},
                                         ^
bad_program.c:27:41: note: (near initialization for ‘customInput.title[1]’)
bad_program.c:28:19: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
         .extra = {&customInputExtra[0], &customInputExtra[1]},
                   ^
bad_program.c:28:19: note: (near initialization for ‘customInput.extra[0]’)
bad_program.c:28:41: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
         .extra = {&customInputExtra[0], &customInputExtra[1]},
                                         ^
bad_program.c:28:41: note: (near initialization for ‘customInput.extra[1]’)
bad_program.c: In function ‘main’:
bad_program.c:36:5: warning: implicit declaration of function ‘memset’ [-Wimplicit-function-declaration]
     memset(&customInputTitle, 0, CUSTOM_INPUT_LENGTH << 1);
     ^~~~~~
bad_program.c:36:5: warning: incompatible implicit declaration of built-in function ‘memset’
bad_program.c:36:5: note: include ‘<string.h>’ or provide a declaration of ‘memset’
bad_program.c:25:33: warning: format ‘%d’ expects argument of type ‘int *’, but argument 3 has type ‘inputType * {aka enum <anonymous> *}’ [-Wformat=]
 #define CUSTOM_INPUT_REGX       "%d=%[^|]s|%[^=]s=%[^|]s|%s"
                                 ^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
     sscanf(payload, CUSTOM_INPUT_REGX,
                     ^~~~~~~~~~~~~~~~~
bad_program.c:25:33: warning: format ‘%[^|’ expects argument of type ‘char *’, but argument 4 has type ‘unsigned char (*)[40]’ [-Wformat=]
 #define CUSTOM_INPUT_REGX       "%d=%[^|]s|%[^=]s=%[^|]s|%s"
                                 ^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
     sscanf(payload, CUSTOM_INPUT_REGX,
                     ^~~~~~~~~~~~~~~~~
bad_program.c:25:33: warning: format ‘%[^=’ expects argument of type ‘char *’, but argument 5 has type ‘unsigned char (*)[40]’ [-Wformat=]
 #define CUSTOM_INPUT_REGX       "%d=%[^|]s|%[^=]s=%[^|]s|%s"
                                 ^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
     sscanf(payload, CUSTOM_INPUT_REGX,
                     ^~~~~~~~~~~~~~~~~
bad_program.c:25:33: warning: format ‘%[^|’ expects argument of type ‘char *’, but argument 6 has type ‘unsigned char (*)[40]’ [-Wformat=]
 #define CUSTOM_INPUT_REGX       "%d=%[^|]s|%[^=]s=%[^|]s|%s"
                                 ^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
     sscanf(payload, CUSTOM_INPUT_REGX,
                     ^~~~~~~~~~~~~~~~~
bad_program.c:25:33: warning: format ‘%s’ expects argument of type ‘char *’, but argument 7 has type ‘unsigned char (*)[40]’ [-Wformat=]
 #define CUSTOM_INPUT_REGX       "%d=%[^|]s|%[^=]s=%[^|]s|%s"
                                 ^
bad_program.c:39:21: note: in expansion of macro ‘CUSTOM_INPUT_REGX’
     sscanf(payload, CUSTOM_INPUT_REGX,
                     ^~~~~~~~~~~~~~~~~
0 голосов
/ 19 декабря 2018

"%d=%[^|]|%[^=]=%[^|]|%s" - правильный формат.

...