Извлечение ключа = значения с помощью scanf в C

#c #string #scanf

#c #строка #scanf

Вопрос:

Мне нужно извлечь значение для данного ключа из строки. Я сделал эту быструю попытку:

 char js[] = "some preceding text withn"
    "new lines and spacesn"
    "param_1=123n"
    "param_2=321n"
    "param_3=stringn"
    "param_2=321n";

char* param_name = "param_2";
char *key_s, *val_s;
char buf[32];

key_s = strstr(js, param_name);

if (key_s == NULL)
    return 0;

val_s = strchr(key_s, '=');

if (val_s == NULL)
    return 0;

sscanf(val_s   1, "1s", buf);

printf("'%s'n", buf);
  

И это на самом деле работает нормально ( printf выдает '321' ). Но я полагаю scanf , что / sscanf сделает эту задачу еще проще, но мне не удалось определить строку форматирования для этого.

Возможно ли передать содержимое переменной param_name , sscanf чтобы она оценивала его как часть строки форматирования? Другими словами, мне нужно указать sscanf , что в этом случае он должен искать шаблон param_2=%s ( param_name фактически он исходит из аргумента функции).

Комментарии:

1. Я думаю, ваш sscanf вызов копирует слишком много, если значение не является последней записью, может быть, вы хотите "1[^n]" ?

2. @mafso, %s остановится при попадании на белый символ (который равен n)

3. Конечно. Извините за глупый комментарий.

4. Обратите внимание, что это «работает», потому что у вас нет param_22=xyz записи (или spare_param_2=abc записи) перед param_2=321 записью.

5. Вероятно, вам лучше всего использовать библиотеку регулярных выражений или какой-либо другой анализатор. Хотя scanf и допускает поведение регулярных выражений, определяемое реализацией, полагаться на это хрупко.

Ответ №1:

Не напрямую, нет.

На практике, конечно, ничто не мешает вам создавать строку формата для sscanf() во время выполнения, например snprintf() .

Что-то вроде:

 void print_value(const char **js, size_t num_js, const char *key)
{
  char tmp[32], value[32];

  snprintf(tmp, sizeof tmp, "%s=%1s", key);
  for(size_t i = 0; i < num_js;   i)
  {
    if(sscanf(js[i], tmp, value) == 1)
    {
      printf("found '%s'n", value);
      break;
    }
  }
}
  

Ответ №2:

У OP есть хороший первый шаг:

 char *key_s = strstr(js, param_name);
if (key_s == NULL)
  return 0;
  

Остальное может быть упрощено до

 if (sscanf(amp;key_s[strlen(param_name)], "=1s", buf) == 0) {
  return 0;
}
printf("'%s'n", buf);
  

В качестве альтернативы можно было бы использовать " =1s" , чтобы разрешить пробелы раньше = .

Подход OP вводит в заблуждение "param_2 321n" "param_3=stringn" .

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

Ответ №3:

Одна из проблем, которая заслуживает рассмотрения, заключается в разнице между поиском параметра «ключ = значение» в строке для определенного значения ключа (например, param_2 в вопросе) и поиском любого параметра «ключ = значение» в строке (без учета конкретного ключа априори).). Используемые методы довольно разные.

Еще одна проблема, которая, очевидно, не рассматривалась, — это возможность того, что вы ищете ключ param_2 , но строка также содержит param_22=xyz и t_param_2=abc . Простые подходы, используемые strstr() для поиска param_2 , подберут любую из этих альтернатив.

В образце данных имеется набор символов, которые не находятся в формате ‘ключ = значение’, которые должны быть пропущены перед любыми частями ‘ключ = значение’. В общем случае мы должны предположить, что такие данные появляются до, между и после пар «ключ = значение». Похоже, что значения не должны поддерживать такие сложности, как строки в кавычках и метасимволы, а значение разделено пробелом. Соглашение о комментариях не отображается.

Вот несколько работоспособных кодов:

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

enum { MAX_KEY_LEN = 31 };
enum { MAX_VAL_LEN = 63 };

int find_any_key_value(const char *str, char *key, char *value);
int find_key_value(const char *str, const char *key, char *value);

int find_any_key_value(const char *str, char *key, char *value)
{
    char junk[256];
    const char *search = str;
    while (*search != '')
    {
        int offset;
        if (sscanf(search, " 1[a-zA-Z_0-9]=cs%n", key, value, amp;offset) == 2)
            return(search   offset - str);
        int rc;
        if ((rc = sscanf(search, "%5s%n", junk, amp;offset)) != 1)
            return EOF;
        search  = offset;
    }

    return EOF;
}

int find_key_value(const char *str, const char *key, char *value)
{
    char found[MAX_KEY_LEN   1];
    int offset;
    const char *search = str;
    while ((offset = find_any_key_value(search, found, value)) > 0)
    {
        if (strcmp(found, key) == 0)
            return(search   offset - str);
        search  = offset;
    }
    return offset;
}

int main(void)
{
    char js[] = "some preceding text withn"
                "new lines and spacesn"
                "param_1=123n"
                "param_2=321n"
                "param_3=stringn"
                "param_4=param_2=confusionn"
                "m= xn"
                "param_2=987n";
    const char p2_key[] = "param_2";
    int offset;
    const char *str;
    char key[MAX_KEY_LEN   1];
    char value[MAX_VAL_LEN   1];

    printf("String being scanned is:n[[%s]]n", js);

    str = js;
    while ((offset = find_any_key_value(str, key, value)) > 0)
    {
        printf("Any found key = [%s] value = [%s]n", key, value);
        str  = offset;
    }

    str = js;
    while ((offset = find_key_value(str, p2_key, value)) > 0)
    {
        printf("Found key %s with value = [%s]n", p2_key, value);
        str  = offset;
    }

    return 0;
}
  

Пример вывода:

 $ ./so24490410
String being scanned is:
[[some preceding text with
new lines and spaces
param_1=123
param_2=321
param_3=string
param_4=param_2=confusion
m= x
param_2=987
]]
Any found key = [param_1] value = [123]
Any found key = [param_2] value = [321]
Any found key = [param_3] value = [string]
Any found key = [param_4] value = [param_2=confusion]
Any found key = [m] value = [x]
Any found key = [param_2] value = [987]
Found key param_2 with value = [321]
Found key param_2 with value = [987]
$
  

Если вам нужно обрабатывать разные длины ключей или значений, вам необходимо настроить строки формата, а также перечисления. Если вы передаете размер ключевого буфера и размер буфера значений функциям, то вам нужно использовать snprint() для создания строк формата, используемых sscanf() . Существует вероятность того, что у вас может быть одно «слово» из 255 символов, за которым сразу следует целевая строка «ключ = значение». Шансы смехотворно малы, но вы можете решить, что вам нужно беспокоиться об этом (это предотвращает защиту этого кода от бомб).