【问题标题】:Split string into two variables in C在C中将字符串拆分为两个变量
【发布时间】:2020-04-11 04:44:35
【问题描述】:

我一直在分配一个任务,它使用 C 读取给定文件并将数据输入到二叉树中。我当前的问题是将从文件中读取的行拆分为两个不同的变量。

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

目前,程序正在正确读取文件并存储每一行​​,然后显示它。我曾尝试使用令牌、memmove 并尝试简单地手动选择字符,但这需要是动态的。 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 变量中。

非常感谢您的帮助。

【问题讨论】:

  • 看看strtokTHIS
  • 如果您想将直到第一个 , 的所有内容转换为整数,请查看 sscanf()fscanf(),顺便说一下,您的 filename 变量没有任何作用,直接做fopen("file.txt", "r");。尽可能删除数组是个好主意。
  • 在您的代码中,while (line != NULL)line 是一个数组,并将衰减为指向其第一个元素的指针,因此它永远不会NULL

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


【解决方案1】:

就像@LPs 建议的那样,假设每一行都像“2019, blah, blah, blah”,您可以通过调用获取每行的 ID:

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

【讨论】:

  • 我以前用过这个,但是它给出了一个我不记得的错误。由于某种原因,它不喜欢 atoi
【解决方案2】:

如果想解析类似的文件,

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

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

#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;
}

打印,

#2409: <blah>, <blah>, <blah>
#16: <foo>, <bar>, <baz>, <qux>
#8: <a\
a>, <b  b\#\\\,still b>, <c>

但这可能有点矫枉过正。

【讨论】:

  • 我很欣赏这项工作,但它确实是矫枉过正。这个程序只是简单的从文件中取出数据,然后输入到二叉树中并显示出来。
  • 如果行是 constant 格式,可以使用sscanf(s, "%d, %s, %s, %s\n", ...) 之类的东西并检查返回;与此相比,这相当简单。
【解决方案3】:

正如@HAL9000 提到的,我能够通过使用 sscanf 来完成这项工作。只需使用sscanf(line, "%d %[^\n]s", &amp;ID, details);从行中提取整数和字符串

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

【讨论】:

    【解决方案4】:

    使用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 ;@#'

    【讨论】:

      猜你喜欢
      • 2020-12-27
      • 1970-01-01
      • 1970-01-01
      • 2021-12-11
      • 1970-01-01
      • 2015-11-30
      • 1970-01-01
      • 2017-08-19
      • 2013-12-28
      相关资源
      最近更新 更多