【问题标题】:Read from file word by word with spaces in C用C中的空格逐字读取文件
【发布时间】:2021-03-31 09:29:54
【问题描述】:

text文件中的input数据如下:

Mike     107        Europe             6798  
Steve    023        New York           2588
Adam     057        South Town         1902
Adam     124        Asia               5555

whitespace分隔的数据

C code从文件中读取数据是:

char name[30];
int code;
char local[30];
int id;


while (fscanf(fp, "%s %d %[^ ] %d", name, &code, local, &id) != EOF){

    printf("%s-%d-%s-%d\n", name, code, local, id);
}

我的代码的问题是,对于local,总是一次读取一个单词,我的打印输出是:

Mike     107        Europe             6798  
Steve    023        New                2588
Adam     057        South              1902
Adam     124        Asia               5555

New YorkSouth Town 被截断。我该如何解决?

【问题讨论】:

  • %[^ ] 的格式与%s 完全相同,即读取到下一个空格。我推荐你read whole lines,然后尝试使用其他方式提取字段。
  • 似乎在这个数据空间中不是真正的分隔符,您是使用列号来分隔字段还是TAB字符。如果您可能同时使用 TAB 和 SPC,那么您确实遇到了不明确的分隔符问题。
  • 当你声称你会在那里得到数字 2588 时,我不相信你。不,它将是上一行的数字。
  • @kerma 你确定你的输入文件是用空格而不是制表符分隔的吗?
  • @KamilCuk 只有空格没有制表符

标签: c file


【解决方案1】:

我该如何解决?

你最多可以读到两个连续的空格。您必须自己实现这样的逻辑,例如:

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

const char input[] =
    "Mike     107        Europe             6798  \n"
    "Steve    023        New York           2588\n"
    "Adam     057        South Town         1902\n"
    "Adam     124        Asia               5555\n"
    ;

int main() {
    FILE *in = fmemopen((char*)input, sizeof(input) - 1, "r");

    char name[30];
    int code;
    char local[30];
    int id;

    char line[250];
    int localstart;
    const char *localend;
    while (fgets(line, sizeof(line), in) != NULL) {
        if (sscanf(line, "%s%d %n", name, &code, &localstart) != 2) break;
        localend = strstr(line + localstart, "  ");
        if (localend == NULL) break;
        const size_t locallen = localend - (line + localstart);
        memcpy(local, line + localstart, locallen);
        local[locallen] = '\0';
        if (sscanf(line + locallen, "%d", &id) != 1) break;

        printf("%s-%d-%s-%d\n", name, code, local, id);
    }
}

例如,您也可以选择:阅读整行并从后面检查是否。第 3 列在第 2 列之后的空格之后和第 4 列之前的空格之前开始。

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

const char input[] =
    "Mike     107        Europe             6798  \n"
    "Steve    023        New York           2588\n"
    "Adam     057        South Town         1902\n"
    "Adam     124        Asia               5555\n"
    ;

int main() {
    FILE *in = fmemopen((char*)input, sizeof(input) - 1, "r");

    char name[30];
    int code;
    char local[30];
    int id;

    char line[250];
    int localstart;
    while (fgets(line, sizeof(line), in) != NULL) {
        if (sscanf(line, "%s%d %n", name, &code, &localstart) != 2) break;
        const char *end = line + strlen(line) - 1;
        // ignore trailing spaces
        while (end != line && isspace(*end)) end--;
        // jump over last digit
        while (end != line && isdigit(*end)) end--;
        // scan the last digit
        if (sscanf(end + 1, "%d", &id) != 1) break;
        // rewind spaces
        while (end != line && isspace(*end)) end--;
        // at end + 1 ends 'local' column
        const char *localend = end + 1;
        const size_t locallen = localend - (line + localstart);
        memcpy(local, line + localstart, locallen);
        local[locallen] = '\0';

        printf("%s-%d-%s-%d\n", name, code, local, id);
    }
}

fmemopen 函数在我用于测试的 linux 上可用 - 它可能在 windows 上不可用。

【讨论】:

    【解决方案2】:

    您的数据似乎需要两种方式 - 将空格用作分隔符和内容。这充其量只是一个模棱两可的设计决策,而 CSV 可能是一个更好的解决方案。但是,您可以对至少在示例输入中解决问题的数据内容进行大概的假设。

    假设local列不能包含数字字符,那么:

    fscanf(fp, "%s %d %[^0123456789\t] %d", name, &code, local, &id)
    

    我已包含\t (TAB) 以确保它不会出错。但是,这样做的效果是 local 包括所有尾随空格。你可以这样修剪它们:

    for( int i = strlen(local) - 1; local[i] == ' '; i-- )
    {
        local[i] = 0 ;
    }
    

    或者,您可以读取整行并对其进行处理,以使单个空格不是分隔符,而 >1 个空格或 TAB 是。或者,如果您可以保证没有 TAB,您可以按列号提取字段数据,但这一切都变得相当繁琐:

        while( fgets( line, sizeof(line), fp ) != NULL )
        {
            line[NAME_COL-1] = 0 ;
            line[CODE_COL-1] = 0 ;
            line[ID_COL-1] = 0 ;
            sscanf(&s[NAME_COL], "%s", name) ;
            sscanf(&s[CODE_COL], "%d", &code) ;
            sscanf(&s[LOCAL_COL], "%s", local) ;
            sscanf(&s[ID_COL], "%d", &id) ;
            for( int i = strlen(local) - 1; local[i] == ' '; i-- )
            {
                local[i] = 0 ;
            }
            printf("%s-%d-%s-%d\n", name, code, local, id);
        }
    

    【讨论】:

    • @G.Sliepen 你是什么意思?我已经测试过这个解决方案,但没有奏效,同样的输出!?
    • @G.Sliepen - 已修复 - 问题是它包含local 中的所有尾随空格。正在努力。
    • @G.Sliepen :最简单的解决方案似乎是去除尾随空格。
    猜你喜欢
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    • 2018-12-15
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 2011-04-12
    相关资源
    最近更新 更多