在C中将字符串拆分为两个变量

时间:2019-12-18 15:29:24

标签: c string char int binary-search-tree

我得到了一个分配,该分配使用C读取给定文件并将数据输入到二进制树中。我当前的问题是将从文件中读取的行拆分为两个不同的变量。

已给出的文件包含两位数据,一个ID和一些信息。 2409, blah, blah, blah

当前,程序正在正确读取文件并存储每一行​​,然后显示它。我尝试使用令牌的,记忆式的,并尝试简单地手动选择字符,但这必须是动态的。 ID不是固定数量的数字,因此手动选择它将不起作用。如前所述,我尝试使用strtok使用“,”作为分隔符,但是它什么都没有改变。

这是当前用于显示信息的内容,我打算在while循环中为每一行拆分字符串:

int main() {

    struct node* root = NULL;

    FILE *file;
    char filename[15];
    char buff[255];
    char line[128];

    strcpy(filename, "file.txt");

    file = fopen(filename, "r");
    if (file == NULL) {
        printf("File could not be openned.\n");
        exit(0);
    }

    while (line != NULL)
    {
        strcpy(line, fgets(buff, 255, file));

        printf("%s", line);
    }
    fclose(file);
}

有什么方法可以简单地选择第一个字符,直到“”第一次出现,然后将它们转换为整数。然后选择其余数据,删除第一个“ ID”,并将其插入char变量中。

非常感谢您的帮助。

4 个答案:

答案 0 :(得分:1)

建议使用@LP,假设每行都类似于“ 2019,blah,blah,blah”,则可以通过调用以下内容获取每行的ID:

   int id = atoi(strtok(line, ","));

答案 1 :(得分:1)

如果要解析类似的文件,

.container

仅使用lex and yacc之类的解析器生成器或我最喜欢的re2c这样的解析器生成器可能会更好。

2409, blah, blah, blah
   0x10,foo,    bar,    baz, qux
# This is more difficult.
    010   , a\
a,   b  b\#\\\,still b,c

打印

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <assert.h>

/* Tokens. */
#define PARAM(A) A
#define STRINGISE(A) #A
#define TOKENS(X) X(ERROR), X(END), X(COMMA), X(NEWLINE), \
    X(ESCAPE), X(WSP), X(NUMBER), X(WORD)
enum Token { TOKENS(PARAM) };
static const char *const tokens[] = { TOKENS(STRINGISE) };

struct Lexer { size_t line; char *marker, *from, *cursor; };

static enum Token lex(struct Lexer *lexer) {
    assert(lexer);
/*!re2c
    re2c:yyfill:enable   = 0;
    re2c:define:YYCTYPE  = char;
    re2c:define:YYCURSOR = lexer->cursor;
    re2c:define:YYMARKER = lexer->marker; // Rules overlap.

    newline = "\n" | ("\r" "\n"?);
    oct = "0" [0-7]*;
    dec = [1-9][0-9]*;
    hex = '0x' [0-9a-fA-F]+;
    num = oct | dec | hex;
    word = [^\x00\\\n\r \t\v\f,0-9]+;
    comment = "#" [^\x00\n\r]* newline;
*/
scan:
    lexer->from = lexer->cursor;
/*!re2c
    * { return ERROR; }
    "\x00" { return END; }
    [ \t\v\f]+ { return WSP; }
    newline { lexer->line++; return NEWLINE; }
    "\\\n" | comment { lexer->line++; goto scan; }
    "\\\\" | "\\," | "\\ " | "\\n" | "\\#" { return ESCAPE; }
    "," { return COMMA; }
    word { return WORD; }
    num { return NUMBER; }
*/
}

struct Buffer {
    char *data;
    size_t size, capacity;
};

static char *buffer_reserve(struct Buffer *const buf, const size_t reserve) {
    const size_t min = buf->size + reserve;
    size_t c = buf->capacity;
    char *data;
    assert(buf);
    if(reserve > (size_t)-1 - buf->size || min > ((size_t)-1 >> 1) + 1)
        { errno = ERANGE; return 0; }
    if(min > c) {
        if(!c) c = 1;
        while(min <= c) c <<= 1;
        if(!(data = realloc(buf->data, c))) return 0;
        buf->data = data;
        buf->capacity = c;
    }
    return buf->data + buf->size;
}

struct Word { char *start, *end; };

struct Parser {
    int id, id_set, first_comma;
    size_t num_words;
    struct Word words[64]; /* Lazy. */
    char *start_words, *end_words;
};
static size_t parser_max_words = sizeof ((struct Parser *)0)->words
    / sizeof *((struct Parser *)0)->words;

static void clear_parser(struct Parser *const parser) {
    assert(parser);
    parser->id_set = 0;
    parser->first_comma = 1;
    parser->num_words = 0;
    parser->start_words = parser->end_words = 0;
}
static void print_parser(const struct Parser *const parser) {
    const struct Word *word = parser->words,
        *word_end = parser->words + parser->num_words;
    assert(parser && parser->id_set && parser->num_words <= parser_max_words);
    printf("#%d: ", parser->id);
    for( ; word < word_end; word++) {
        if(word != parser->words) printf(", ");
        if(!word->start) { printf("<null>"); continue; }
        assert(word->start <= word->end);
        if(word->start == word->end) { printf("<empty>"); continue; }
        printf("<%.*s>", (int)(word->end - word->start), word->start);
    }
    fputc('\n', stdout);
}
static void expand_word(struct Parser *const parser,
    const struct Lexer *const lexer) {
    assert(parser && lexer && lexer->from < lexer->cursor);
    if(!parser->start_words) {
        assert(!parser->end_words);
        parser->start_words = lexer->from;
    }
    parser->end_words = (lexer->from + INT_MAX >= lexer->cursor) ?
        lexer->cursor : lexer->from + INT_MAX;
}
static int store_word(struct Parser *const parser) {
    struct Word *word;
    assert(parser);
    if(parser->num_words >= parser_max_words) return errno = EILSEQ, 0;
    word = parser->words + parser->num_words++;
    word->start = parser->start_words;
    word->end = parser->end_words;
    parser->start_words = parser->end_words = 0;
    return 1;
}

int main(int argc, char **argv) {
    const size_t granularity = 1024;
    struct Lexer lexer = { 1, 0, 0, 0 };
    struct Parser parser;
    size_t nread;
    struct Buffer buf = { 0, 0, 0 };
    char *b;
    FILE *fp = 0;
    int success = 0, end_of_buffer = 0;

    /* Open. */
    if(argc != 2) return fprintf(stderr, "Needs filename.\n"), EXIT_FAILURE;
    if(!(fp = fopen(argv[1], "r"))) goto catch;

    /* Read. */
    do {
        if(!(b = buffer_reserve(&buf, granularity))) goto catch;
        nread = fread(b, 1, granularity, fp);
        buf.size += nread;
    } while(nread == granularity);
    if(ferror(fp)) goto catch;
    fclose(fp), fp = 0;
    if(!(b = buffer_reserve(&buf, 1))) goto catch;
    *b = '\0'; /* Make sure it's a string. */

    /* Parse. */
    lexer.cursor = buf.data;
    clear_parser(&parser);
    do {
        enum Token tok;
        switch((tok = lex(&lexer))) {
        case ERROR: goto catch;
        case END: end_of_buffer = 1; break;
        case COMMA:
            if(!parser.id_set) { errno = EILSEQ; goto catch; }
            if(parser.first_comma) { parser.first_comma = 0; break; }
            if(!store_word(&parser)) goto catch;
            break;
        case NEWLINE:
            if(parser.id_set) {
                /* We require at least key, data. */
                if(!store_word(&parser)) goto catch;
                print_parser(&parser);
                clear_parser(&parser);
            } else if(parser.start_words) {
                errno = EILSEQ; goto catch;
            }
            break;
        case ESCAPE:
            if(!parser.id_set) { errno = EILSEQ; goto catch; }
            expand_word(&parser, &lexer);
            break;
        case WSP: break;
        case NUMBER:
            if(parser.id_set) {
                expand_word(&parser, &lexer);
            } else {
                char *end;
                long i = strtol(lexer.from, &end, 0);
                if(end != lexer.cursor || i < INT_MIN || i > INT_MAX)
                    { errno = EDOM; goto catch; }
                parser.id = (int)i;
                parser.id_set = 1;
            }
            break;
        case WORD:
            expand_word(&parser, &lexer);
            break;
        }
    } while(!end_of_buffer);
    success = EXIT_SUCCESS;
    goto finally;
catch:
    fprintf(stderr, "While on line %lu.\n", (unsigned long)lexer.line);
    perror("parsing");
    assert(!lexer.from || (lexer.from < lexer.cursor
        && lexer.from + INT_MAX >= lexer.cursor));
    if(lexer.from) fprintf(stderr, "While on %.*s.\n",
        (int)(lexer.cursor - lexer.from), lexer.from);
finally:
    free(buf.data);
    if(fp) fclose(fp);
    return success;
}

但这可能是过分的。

答案 2 :(得分:1)

正如@ HAL9000所述,我能够使用sscanf完成此操作。只需使用sscanf(line, "%d %[^\n]s", &ID, details);

从行中提取整数和字符串

我确实尝试过使用strtok,但是由于它无法正常工作,我无法理解。 sscanf最容易做到,这就是我要使用的,谢谢。

答案 3 :(得分:0)

使用sscanf

例如

int main(int argc, char *argv[]) {
    const char *str = "123, this, is, a test ;@#";
    char buff[128] = {0};
    int num = 0;

    if (2 == sscanf(str, "%d,%[^\r\n]s", &num, buff))
        printf("== num: %d, string: '%s'\n", num, buff);
    else
        printf("== Wrong!\n");
    return 0;
}

结果:== num: 123, string: ' this, is, a test ;@#'