Проблема с совпадением пустой строки с scanf в специальном формате - PullRequest
0 голосов
/ 09 января 2019

У меня CUSTOM_PROMPT_REGX шаблон с особыми условиями.

Предполагается захватить 10 текстов, которые следуют друг за другом с | или # в качестве разделителей. Каждый из них может быть пустым, поэтому между | или # нет символов, и он будет выглядеть как "..|#..."

Мой код:

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


#define CUSTOM_PROMPT_REGX  "@%39[^|]|%39[^#]#%39[^|]|%39[^#]#%39[^|]|%39[^#]#%39[^|]|%39[^#]#%39[^|]|%39[^@]@"
static unsigned char lines[5][2][40];

int main(void)
{
    memset(lines, 0, sizeof(lines));
    int j = sscanf("@1.SALAM|818BF4F2A8#2.BINGO|828BF8F0F7FE93#3.GOOGLE|838BF1F0F8F0#|#5.WINE|858BF6FE90F8@", CUSTOM_PROMPT_REGX,
           lines[0][0], lines[0][1], lines[1][0], lines[1][1],
           lines[2][0], lines[2][1], lines[3][0], lines[3][1], lines[4][0], lines[4][1]);

    printf("%d\n[%s <=> %s]\n[%s <=> %s]\n[%s <=> %s]\n[%s <=> %s]\n[%s <=> %s]\n", j,
           lines[0][0], lines[0][1], lines[1][0], lines[1][1], lines[2][0], lines[2][1],
           lines[3][0], lines[3][1], lines[4][0], lines[4][1]);
    return 0;
}

и результат:

6
[1.SALAM <=> 818BF4F2A8]
[2.BINGO <=> 828BF8F0F7FE93]
[3.GOOGLE <=> 838BF1F0F8F0]
[ <=> ]
[ <=> ]
Press <RETURN> to close this window...

Должно быть:

8
[1.SALAM <=> 818BF4F2A8]
[2.BINGO <=> 828BF8F0F7FE93]
[3.GOOGLE <=> 838BF1F0F8F0]
[ <=> ]
[5.WINE <=> 858BF6FE90F8]

Есть ли что-то, что я могу добавить к CUSTOM_PROMPT_REGX, чтобы решить мою проблему?

1 Ответ

0 голосов
/ 09 января 2019

Каждый из них может быть пустым, поэтому между ними нет символов ...
... есть что-то, что я могу добавить в CUSTOM_PROMPT_REGX, чтобы решить мою проблему?

Нет. %[...] останавливает все sscanf(), когда ничего не сканируется в спецификатор. По крайней мере 1 символ должен соответствовать сканирующему набору .

Альтернативы:

  1. Сканирование с использованием одной директивы %[...] за раз. Для этого достаточно просто сделать цикл.

  2. Используйте подход не sscanf(). Исследования strtok(), strspn(), strcspn().

  3. Сканирование символа разделителя отведений в строку и последующее использование строки, начиная с индекса 1. В случае OP, 2 из 3 разделителей не используются последовательно, так что это возможный подход.

  4. Сканирование на 5 групп на "%79[^#]#, а затем подразделить каждую. Исследования strchr(buf80, '|');


Совет

Сложные sscanf() форматы легче кодировать, просматривать и обслуживать, используя строковый литерал конкатенацию.

#define VB_FMT "%39[^|]|"
#define LB_FMT "%39[^#]#"
#define AT_FMT "%39[^@]@"
#define CUSTOM_PROMPT_REGX  "@" \
    VB_FMT LB_FMT VB_FMT LB_FMT VB_FMT LB_FMT VB_FMT LB_FMT VB_FMT AT_FMT

Пример кода, выполняющего 1 sscanf() "%[]" определитель за раз.

int main() {
  #define ATVB_FMT "@%n%39[^|]%n"
  #define VBLB_FMT "|%n%39[^#]%n"
  #define LBVB_FMT "#%n%39[^|]%n"
  #define VBAT_FMT "|%n%39[^@]@%n"
  #define N 10

  const char *fmt[10] = {ATVB_FMT, VBLB_FMT, LBVB_FMT, VBLB_FMT, LBVB_FMT,
      VBLB_FMT, LBVB_FMT, VBLB_FMT, LBVB_FMT, VBAT_FMT};

  char lines[N][40];

  const char *buf = \
      "@1.SALAM|818BF4F2A8#2.BINGO|828BF8F0F7FE93#3.GOOGLE|838BF1F0F8F0#|#5.WINE|858BF6FE90F8@";
  const char *s = buf;

  for (int i = 0; i<N; i++) {
    int n1 = 0;
    int n2 = 0;
    sscanf(s, fmt[i], &n1, lines[i], &n2);
    if (n1 == 0) {
      fprintf(stderr, "Failed to find separator %d\n", i);
      return EXIT_FAILURE;
    }
    if (n2 == 0) {
      lines[i][0] = '\0';
      s += n1;
    } else {
      s += n2;
    }
  }

  if (*s) {
    fprintf(stderr, "Failed end %d\n", N);
    return EXIT_FAILURE;
  }

  for (int i = 0; i<N; i++) {
    printf("<%s>\n", lines[i]);
  }
return 0;
}

выход

<1.SALAM>
<818BF4F2A8>
<2.BINGO>
<828BF8F0F7FE93>
<3.GOOGLE>
<838BF1F0F8F0>
<>
<>
<5.WINE>
<858BF6FE90F8>
...