C从文件读取以空格分隔的值 [英] C read space-separated values from file

查看:118
本文介绍了C从文件读取以空格分隔的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要从文件中读取文本,并根据读取的信息将值分配给结构.

以下是文本文件的格式:

First Middle Last   Address          city     state zip age sex tenure salary  
\--------------------------------------------------------------  
ADA     A AGUSTA    33 BABBAGE ROAD  LOVELACE    GB 19569 28 F 2 350.50  
ISSAC   A ASIMOV    99 FICTION WAY   AMHERST     MA 63948 58 M 6 423.88  
HUMPHRY R BOGART    71 SAM STREET    HOLLYWOOD   CA 48482 56 M 5 366.00  

我必须将其读入的结构:

typedef struct  
{  
char first[8], initial, last[10],  
street[17], city[12], state[3];  
int age, tenure, zip;  
float salary;  
char sex;  
}Employee;  

到目前为止,我使用的代码是:

void inputLine(Employee* e)  
{  
fscanf(pay, "%s %s %s %s %s %s %s %s %d %d %s %d %f",  
           e->first, &e->initial, e->last, e->street,  
           e->street, e->city, e->city, e->state,  
           &e->zip, &e->age, &e->sex, &e->tenure,  
           &e->salary);  
}  

但是当我打印每一行时,前几行就可以了,然后事情就开始混乱了:

TED L KOPPEL ABC WASHINGTON DC 37376 48 M 9 909.44  
DAVID T LETTERMAN WNBC NEW YORK 0 0   
NY 1 47 5 STEVIE R 0 0   
NICKS 3 MUSIC CHICAGO 23459 38 0 0   
F 8 460.88 P 76 SILLY 0 89   
STREET L GB 44 2 320.50 0 12341   

我也尝试过:

void inputLine(Employee* e)  
{  
char line[53];  
fgets(line, 52, pay);  
printf("%s\n", line);  
fflush(stdout);  
sscanf(line, "%s %s %s %s %s %s %s %s %d %d %s %d %f",  
e->first, &e->initial, e->last, e->street,  
e->street, e->city, e->city, e->state,  
&e->zip, &e->age, &e->sex, &e->tenure,  
&e->salary);  
}  

但这给出了相同的结果.

该代码不能使用C ++. 我使用的是Linux GNU GCC 4.8.1编译器,但我也在Mac的编译器上对此进行了测试,但无法正常工作.

解决方案

诸如pay的全局变量(明确定义为FILE *pay;)是一个坏主意,在示例代码中完全没有必要.始终测试fscanf()及其亲属的返回值,以确保您获得了所有期望的数据.

但是,您的麻烦是%s在第一个空格处停止,因此您在读取地址字段时遇到了巨大的麻烦.您的输入也不受限制.您还尝试使用e->street多次获取街道地址的多个单词;因为第三个单词会覆盖第一个单词,所以该命令将无效.

您需要类似的东西

int inputLine(FILE *fp, Employee* e)  
{  
    if (fscanf(fp, "%7s %c %9s %16c %11c %2s %d %d %c %d %f",  
               e->first, &e->initial, e->last, e->street, e->city, e->state,  
               &e->zip, &e->age, &e->sex, &e->tenure, &e->salary) != 11)
        return -1;
    e->street[16] = '\0';
    e->city[11] = '\0';
    return 0;
}

这使用%c读取单个字符;它使用%16c读取多字街道地址,并使用%11c读取(可能是多字)城市.它使用%7s%9s%2s来防止其他字符串溢出. fscanf()调用之后的分配确保所计数的%c字符串以null结尾.就其本身而言,%16c不会添加空终止符.

inputLine()函数现在在出现问题时返回错误指示(选择了-1),并返回0指示成功.这是Unix系统调用的常见模式,但与 chux 读取注释空间 . /p>

工作代码1

此代码使用问题中的fscanf()读取标准输入.这样可以确保emp数组也不会溢出.

#include <stdio.h>

typedef struct  
{  
    char first[8], initial, last[10],  
         street[17], city[12], state[3];  
    int age, tenure, zip;  
    float salary;  
    char sex;  
} Employee; 

void dump_employee(FILE *fp, const char *tag, const Employee *e);
int inputLine(FILE *fp, Employee *e);

enum { MAXEMP = 10 };

int main(void)
{
    char line[4096];
    Employee emp[MAXEMP];

    if (fgets(line, sizeof(line), stdin) == 0 ||
        fgets(line, sizeof(line), stdin) == 0)
        return 1;
    for (int i = 0; i < MAXEMP && inputLine(stdin, &emp[i]) != 0; i++)
        dump_employee(stdout, "Employee", &emp[i]);
    return 0;
}

int inputLine(FILE *fp, Employee *e)  
{  
    if (fscanf(fp, "%7s %c %9s %16c %11c %2s %d %d %c %d %f",  
               e->first, &e->initial, e->last, e->street, e->city, e->state,  
               &e->zip, &e->age, &e->sex, &e->tenure, &e->salary) != 11)
        return -1;
    e->street[16] = '\0';
    e->city[11] = '\0';
    return 0;
}

void dump_employee(FILE *fp, const char *tag, const Employee *e)  
{  
    fprintf(fp, "%s: %-7s %c %-9s %-16s %-11s %-2s %.5d %3d %c %d %6.2f\n",  
               tag, e->first, e->initial, e->last, e->street, e->city, e->state,  
               e->zip, e->age, e->sex, e->tenure, e->salary);
}

样本输出

Employee: ADA     A AGUSTA    33 BABBAGE ROAD  LOVELACE    GB 19569  28 F 2 350.50
Employee: ISSAC   A ASIMOV    99 FICTION WAY   AMHERST     MA 63948  58 M 6 423.88
Employee: HUMPHRY R BOGART    71 SAM STREET    HOLLYWOOD   CA 48482  56 M 5 366.00

工作代码2

此代码使用fgets()读取行,并使用sscanf()转换数据.明智地使用此版本的代码来报告错误会更加容易.

#include <stdio.h>

typedef struct  
{  
    char first[8], initial, last[10],  
         street[17], city[12], state[3];  
    int age, tenure, zip;  
    float salary;  
    char sex;  
} Employee; 

void dump_employee(FILE *fp, const char *tag, const Employee *e);
int scan_employee(Employee *e, const char *line);

enum { MAXEMP = 10 };

int main(void)
{
    char line[4096];
    Employee emp[MAXEMP];

    if (fgets(line, sizeof(line), stdin) == 0 ||
        fgets(line, sizeof(line), stdin) == 0)
        return 1;
    for (int i = 0; i < MAXEMP && fgets(line, sizeof(line), stdin) != 0; i++)
    {
        if (scan_employee(&emp[i], line) == 0)
            dump_employee(stdout, "Employee", &emp[i]);
    }
    return 0;
}

int scan_employee(Employee *e, const char *line)  
{  
    if (sscanf(line, "%7s %c %9s %16c %11c %2s %d %d %c %d %f",  
               e->first, &e->initial, e->last, e->street, e->city, e->state,  
               &e->zip, &e->age, &e->sex, &e->tenure, &e->salary) != 11)
        return -1;
    e->street[16] = '\0';
    e->city[11] = '\0';
    return 0;
}

void dump_employee(FILE *fp, const char *tag, const Employee *e)  
{  
    fprintf(fp, "%s: %-7s %c %-9s %-16s %-11s %-2s %.5d %3d %c %d %6.2f\n",  
               tag, e->first, e->initial, e->last, e->street, e->city, e->state,  
               e->zip, e->age, e->sex, e->tenure, e->salary);
}

对于问题中的样本数据,此输出与其他输出相同.

I need to read text from a file and assign values to a struct based on information read.

Here is the format of the text file:

First Middle Last   Address          city     state zip age sex tenure salary  
\--------------------------------------------------------------  
ADA     A AGUSTA    33 BABBAGE ROAD  LOVELACE    GB 19569 28 F 2 350.50  
ISSAC   A ASIMOV    99 FICTION WAY   AMHERST     MA 63948 58 M 6 423.88  
HUMPHRY R BOGART    71 SAM STREET    HOLLYWOOD   CA 48482 56 M 5 366.00  

And the struct I have to read it into:

typedef struct  
{  
char first[8], initial, last[10],  
street[17], city[12], state[3];  
int age, tenure, zip;  
float salary;  
char sex;  
}Employee;  

The code I have used so far has been:

void inputLine(Employee* e)  
{  
fscanf(pay, "%s %s %s %s %s %s %s %s %d %d %s %d %f",  
           e->first, &e->initial, e->last, e->street,  
           e->street, e->city, e->city, e->state,  
           &e->zip, &e->age, &e->sex, &e->tenure,  
           &e->salary);  
}  

But when I print each line, the first few lines are fine, then things start messing up:

TED L KOPPEL ABC WASHINGTON DC 37376 48 M 9 909.44  
DAVID T LETTERMAN WNBC NEW YORK 0 0   
NY 1 47 5 STEVIE R 0 0   
NICKS 3 MUSIC CHICAGO 23459 38 0 0   
F 8 460.88 P 76 SILLY 0 89   
STREET L GB 44 2 320.50 0 12341   

I have also tried:

void inputLine(Employee* e)  
{  
char line[53];  
fgets(line, 52, pay);  
printf("%s\n", line);  
fflush(stdout);  
sscanf(line, "%s %s %s %s %s %s %s %s %d %d %s %d %f",  
e->first, &e->initial, e->last, e->street,  
e->street, e->city, e->city, e->state,  
&e->zip, &e->age, &e->sex, &e->tenure,  
&e->salary);  
}  

But that gives the same result.

The code cannot be in C++. I am using the Linux GNU GCC 4.8.1 compiler, but I have also tested this on Mac's compiler and it did not work.

解决方案

Global variables such as pay — clearly defined as FILE *pay; — are a bad idea, and completely unnecessary in the example code. Always test the return value from fscanf() and its relatives to ensure that you got all the data you expected.

However, your trouble is that %s stops at the first space, so you have immense problems reading the address field. Your inputs are unconstrained too. You also try to get multiple words of the street address using e->street multiple times; that won't work since the third word will overwrite the first.

You need something like:

int inputLine(FILE *fp, Employee* e)  
{  
    if (fscanf(fp, "%7s %c %9s %16c %11c %2s %d %d %c %d %f",  
               e->first, &e->initial, e->last, e->street, e->city, e->state,  
               &e->zip, &e->age, &e->sex, &e->tenure, &e->salary) != 11)
        return -1;
    e->street[16] = '\0';
    e->city[11] = '\0';
    return 0;
}

This uses %c to read single characters; it uses %16c to read the multi-word street address and %11c to read the (possibly multi-word) city. It uses %7s, %9s, and %2s to prevent overflows of other strings. The assignments after the fscanf() call ensure that the counted %c strings are null terminated; by itself, %16c does not add a null terminator.

The inputLine() function now returns an error indication (-1 chosen) when there's a problem, and 0 to indicate success. This is a common pattern with the Unix system calls, but different from the behaviour of the underlying scanf()-family of functions, as noted in a comment by chux.

Working code 1

This code reads standard input, using fscanf() as in the question. It ensures that there's no overflow of the emp array, too.

#include <stdio.h>

typedef struct  
{  
    char first[8], initial, last[10],  
         street[17], city[12], state[3];  
    int age, tenure, zip;  
    float salary;  
    char sex;  
} Employee; 

void dump_employee(FILE *fp, const char *tag, const Employee *e);
int inputLine(FILE *fp, Employee *e);

enum { MAXEMP = 10 };

int main(void)
{
    char line[4096];
    Employee emp[MAXEMP];

    if (fgets(line, sizeof(line), stdin) == 0 ||
        fgets(line, sizeof(line), stdin) == 0)
        return 1;
    for (int i = 0; i < MAXEMP && inputLine(stdin, &emp[i]) != 0; i++)
        dump_employee(stdout, "Employee", &emp[i]);
    return 0;
}

int inputLine(FILE *fp, Employee *e)  
{  
    if (fscanf(fp, "%7s %c %9s %16c %11c %2s %d %d %c %d %f",  
               e->first, &e->initial, e->last, e->street, e->city, e->state,  
               &e->zip, &e->age, &e->sex, &e->tenure, &e->salary) != 11)
        return -1;
    e->street[16] = '\0';
    e->city[11] = '\0';
    return 0;
}

void dump_employee(FILE *fp, const char *tag, const Employee *e)  
{  
    fprintf(fp, "%s: %-7s %c %-9s %-16s %-11s %-2s %.5d %3d %c %d %6.2f\n",  
               tag, e->first, e->initial, e->last, e->street, e->city, e->state,  
               e->zip, e->age, e->sex, e->tenure, e->salary);
}

Sample output

Employee: ADA     A AGUSTA    33 BABBAGE ROAD  LOVELACE    GB 19569  28 F 2 350.50
Employee: ISSAC   A ASIMOV    99 FICTION WAY   AMHERST     MA 63948  58 M 6 423.88
Employee: HUMPHRY R BOGART    71 SAM STREET    HOLLYWOOD   CA 48482  56 M 5 366.00

Working code 2

This code uses fgets() to read lines and sscanf() to convert the data. It would be much easier to report errors sanely with this version of the code.

#include <stdio.h>

typedef struct  
{  
    char first[8], initial, last[10],  
         street[17], city[12], state[3];  
    int age, tenure, zip;  
    float salary;  
    char sex;  
} Employee; 

void dump_employee(FILE *fp, const char *tag, const Employee *e);
int scan_employee(Employee *e, const char *line);

enum { MAXEMP = 10 };

int main(void)
{
    char line[4096];
    Employee emp[MAXEMP];

    if (fgets(line, sizeof(line), stdin) == 0 ||
        fgets(line, sizeof(line), stdin) == 0)
        return 1;
    for (int i = 0; i < MAXEMP && fgets(line, sizeof(line), stdin) != 0; i++)
    {
        if (scan_employee(&emp[i], line) == 0)
            dump_employee(stdout, "Employee", &emp[i]);
    }
    return 0;
}

int scan_employee(Employee *e, const char *line)  
{  
    if (sscanf(line, "%7s %c %9s %16c %11c %2s %d %d %c %d %f",  
               e->first, &e->initial, e->last, e->street, e->city, e->state,  
               &e->zip, &e->age, &e->sex, &e->tenure, &e->salary) != 11)
        return -1;
    e->street[16] = '\0';
    e->city[11] = '\0';
    return 0;
}

void dump_employee(FILE *fp, const char *tag, const Employee *e)  
{  
    fprintf(fp, "%s: %-7s %c %-9s %-16s %-11s %-2s %.5d %3d %c %d %6.2f\n",  
               tag, e->first, e->initial, e->last, e->street, e->city, e->state,  
               e->zip, e->age, e->sex, e->tenure, e->salary);
}

The output from this is identical to the output from the other for the sample data in the question.

这篇关于C从文件读取以空格分隔的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆