Использование sscanf для разбора длинной строки слов в одной строке - PullRequest
2 голосов
/ 13 февраля 2020

При разработке небольшой программы для сканирования строк английских слов sh для ключевых элементов данных я выбрал sscanf () для анализа строки. Поскольку в каждой строке существует неизвестное количество слов, sscanf () должен вызываться с максимальным количеством возможных полей, указанных в запросе. Это приводит к длинному и уродливому однострочному утверждению. Более чистая техника - использовать sscanf () для получения одного слова за раз в запрограммированном l oop. К сожалению, невозможно узнать, сколько пробелов sscanf () пропустило, чтобы получить следующее поле. Таким образом, невозможно снова вызвать sscanf () со строковым указателем, который отражает точное место, где остановился sscanf () при предыдущем вызове. Пример кода приведен ниже. Два вопроса: 1) я что-то упустил при использовании sscanf ()? и 2) есть ли лучший способ сделать это в c?

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

/*
 * using sscanf to parse a line (null terminated string) with fields (words)
 * separated by one or more spaces into an array of words (fields).
 */

void main()
{
        int     i,j;
        int     idx;
        char    string[100] = "word1 word2  word3  word4    word5    word6  word7\0";
        char    fields[20][10];
#if 1
        j=sscanf (&string[0], "%s%s%s%s%s%s", &fields[0][0], &fields[1][0], &fields[2][0], &fields[3][0], &fields[4][0], &fields[5][0]);
        printf("sscanf returned: %d\n",j);
#else
/*
 *  this would be the preferred way to parse a long line of words,
 *  but there is no way to know with certainty how many spaces sscanf
 *  skipped over to obtain the next string (word). A modified version
 *  of sscanf that either modified an integer pointer argument or
 *  updated the pointer to the input string (line) would allow
 *  subsequent calls to pick up where the last sscanf call left off.
 *
 */
        for (i=0,idx=0;i<6;i++){
                j=sscanf (&string[idx], "%s", &fields[i][0]);
                idx += strlen(&fields[i][0]);
                printf("sscanf returned: %d\n",j);
                if (j==0)
                        break;
        }
#endif

        for (i=0;i<6;i++){
                printf("%s",&fields[i][0]);
        }
        printf("\n");
        return;
}

1 Ответ

0 голосов
/ 13 февраля 2020

В строковом литерале, используемом в качестве инициализатора

char    string[100] = "word1 word2  word3  word4    word5    word6  word7\0";

явный завершающий ноль является избыточным. Строковый литерал уже содержит конечный ноль, кроме явного конечного ноля.

Вот, пожалуйста.

#include <stdio.h>

int main(void) 
{
    char    string[100] = "word1 word2  word3  word4    word5    word6  word7";
    char s[10];

    const char *p = string;

    for ( int n = 0; sscanf( p, "%s%n", s, &n ) == 1; p += n )
    {
        puts( s );
    }

    return 0;
}

Вывод программы:

word1
word2
word3
word4
word5
word6
word7

Другой подход заключается в использовать либо стандартную функцию strtok, либо пару функций strcspn и strspn.

Например,

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

int main(void) 
{
    char    string[100] = "word1 word2  word3  word4    word5    word6  word7";

    const char *delim = " \t";

    const char *p = strtok( string, delim );
    while ( p != NULL )
    {
        puts( p );
        p = strtok( NULL, delim );
    }

    return 0;
}

Вывод программы такой же, как показано выше.

А вот демонстрационная программа, которая использует стандартные функции strcspn и strspn.

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

int main(void) 
{
    char    string[100] = "word1 word2  word3  word4    word5    word6  word7";

    const char *delim = " \t";

    for ( const char *p = string; *p; )
    {
        p += strspn( p, delim );

        const char *q  = p;

        p += strcspn( p, delim );

        int n = p - q;

        if ( n ) printf( "%*.*s\n", n, n, q );
    }

    return 0;
}

Снова вывод будет таким же, как показано выше.

Pay Обратите внимание, что в этом случае извлеченные слова не заканчиваются нулем. Поэтому, чтобы скопировать их в массив символов в виде строк, вы должны использовать memcpy, а затем добавить скопированные символы с завершающим нулем.

...