【问题标题】:C Program Structures Using File Input And Output使用文件输入和输出的 C 程序结构
【发布时间】:2016-09-19 04:37:54
【问题描述】:

第一次 C 编程课并开始学习编程。我目前正在学习如何在 C 中使用文件输入/输出,这是一项有助于理解此过程的学习任务。

该任务要求程序使用具有随机美国地址的输入文件,然后按邮政编码从小到大的顺序输出地址。由于程序现在没有崩溃,但我得到一个空的输出文件。我可能知道出了什么问题。我相信我必须告诉程序/函数何时开始读取输入文件,然后何时写入,最后何时关闭文件。可能在某处使用“while”语句?

需要什么以及在哪里让这个程序将地址输出到文件中?任何人都可以推荐一个可以添加的函数/语句来实现这一点吗?

非常感谢您为使该程序正常运行所提供的帮助、时间和指导!

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

#define BUFF_SIZE 32
#define STRUCT_SIZE 512

struct info {
    char name[BUFF_SIZE];
    char stAddress[BUFF_SIZE];
    char cityAndState[BUFF_SIZE];
    char zip[BUFF_SIZE];
};

void selectionSort(struct info *ptrStruct[], int size);

int main(int argc, char *argv[]) {

    FILE *fpin, *fpout;
    int count, size;
    char buffer[512];
    struct info *ptrStruct[STRUCT_SIZE];

    if (argc != 3) {

        printf("Usage: program, inputfile, outputfile\n");
        exit(1);
    }

    if ((fpin = fopen(argv[1], "r")) == NULL) {

        printf("Can't open input file\n");
        exit(1);

    }

    if ((fpout = fopen(argv[2], "w")) == NULL) {

        printf("Can't open output file\n");
        exit(1);
    }

    for (count = 0; count < STRUCT_SIZE; count++){
        ptrStruct[count] = (struct info*) malloc(sizeof(struct info));
        if (EOF == scanf("%599[^\n]%*c", buffer)){
            free(ptrStruct[count]);
            break;
        };
        strcpy(ptrStruct[count]->name, buffer);
        scanf("%511[^\n]%*c", buffer);
        strcpy(ptrStruct[count]->stAddress, buffer);
        scanf("%511[^\n]%*c", buffer);
        strcpy(ptrStruct[count]->cityAndState, buffer);
        scanf("%511[^\n]%*c", buffer);
        strcpy(ptrStruct[count]->zip, buffer);
    }

    size = count;
    selectionSort(ptrStruct, size);

    printf("\n\nLEAST TO GREATEST\n");
    for (count = 0; count < size; count++)
    {
        printf("%s\n", ptrStruct[count]->name);
        printf("%s\n", ptrStruct[count]->stAddress);
        printf("%s\n", ptrStruct[count]->cityAndState);
        printf("%s\n", ptrStruct[count]->zip);
        free(ptrStruct[count]);
    }

fclose(fpin);
fclose(fpout);

}

void selectionSort(struct info *ptrStruct[], int size)
{
    int count1, count2;
    int minIndex;
    struct info *ptrTemporary;

    for (count2 = 0; count2 < size - 1; count2++)
    {
        minIndex = count2;
        for (count1 = count2 + 1; count1 < size; count1++)
        {
            if (strcmp(ptrStruct[count1]->zip, ptrStruct[minIndex]->zip) < 0)
                minIndex = count1;
        }
        if (minIndex != count2){
            ptrTemporary = ptrStruct[count2];
            ptrStruct[count2] = ptrStruct[minIndex];
            ptrStruct[minIndex] = ptrTemporary;
        }
    }
}

使用的输入:

A1, A2
20294 Lorenzana Dr
Woodland Hills, CA
91364
B1, B2
19831 Henshaw St
Culver City, CA
94023
C1, C2
5142 Dumont Pl
Azusa, CA
91112
D1, D2
20636 De Forest St
Woodland Hills, CA
91364
A1, A2
20294 Lorenzana Dr
Woodland Hills, CA
91364
E1, E2
4851 Poe Ave
Woodland Hills, CA
91364
F1, F2
20225 Lorenzana Dr
Los Angeles, CA
91111
G1, G2
20253 Lorenzana Dr
Los Angeles, CA
90005
H1, H2
5241 Del Moreno Dr
Los Angeles, CA
91110
I1, I2
5332 Felice Pl
Stevenson Ranch, CA
94135
J1, J2
5135 Quakertown Ave
Thousand Oaks, CA
91362
K1, K2
720 Eucalyptus Ave 105
Inglewood, CA
89030
L1, L2
5021 Dumont Pl
Woodland Hills, CA
91364
M1, M2
4819 Quedo Pl
Westlake Village, CA
91362
I1, I2
5332 Felice Pl
Stevenson Ranch, CA
94135
I1, I2
5332 Felice Pl
Stevenson Ranch, CA
94135
N1, N2
20044 Wells Dr
Beverly Hills, CA
90210
O1, O2
7659 Mckinley Ave
Los Angeles, CA
90001

【问题讨论】:

  • %*c 中的 * 不会抑制在 scanf 调用中将值分配给缓冲区吗?
  • "%599[^\n]%*c" --> "%511[^\n]%*c", scanf --> fscanf, printf-> fprintf.
  • @BLUEPIXY:谢谢!我忘了删除那条评论

标签: c file loops io structure


【解决方案1】:

首先mainint 类型的函数,并且return 应该是shell 的一个值。虽然并非在所有情况下都是强制性的,但最好初始化所有变量(尤其是当您不熟悉 C 时)。

虽然不是错误,但 C 的标准编码风格避免了 caMelCase 变量,而支持所有 小写。参见例如NASA - C Style Guide, 1994

您的读取循环表明您完全不知道如何将数据从输入文件中获取到程序中。你有一个BUFF_SIZE 常量32,但显然返回并在512 处硬编码buffer,猜测这可能有助于你输入程序。 (你永远无法猜测到 C 中有效解决方案的方式......)

您最长的输入行是 22 个字符,因此 +1 用于 '\n'+1 用于 '\0' nul-terminating 字符意味着您的最长行将需要 24用于存储的字符。这不仅仅是BUFF_SIZE 32 所涵盖的内容。

您的输入例程要求您一次读取一行。这最好使用像fgetsgetline 这样的面向行的输入函数来完成。此外,您需要确保构成地址的所有 4 行 一起读取,并且全部成功读取或不存储任何数据。 (您不希望存储部分地址)。如果您只是检查每一个并且在任何一个上都失败(并继续循环),那么您的nameaddresscitystatezip 将在下一次读取时不同步。

以有意义的方式完成此操作的方法是使用 info 结构的静态实例来构建每个地址。​​然后,一旦所有成员都包含经过验证的值,将该信息添加到您的指针大批。

例如,您的读取循环可以执行如下操作:

for (idx = 0; idx < MAXS;) {
    int a1, a2, a3, a4;
    char buf[MAXB] = "";
    struct info tmp = { .name = "" };

    if (!fgets (buf, MAXB, fp)) break; /* read/validate lines */
    a1 = ((buf[MAXB - 2] && buf[MAXB - 2] != '\n') || 
           !sscanf (buf, "%31[^\n]", tmp.name));

    if (!fgets (buf, MAXB, fp)) break;
    a2 = ((buf[MAXB - 2] && buf[MAXB - 2] != '\n') ||
          !sscanf (buf, "%31[^\n]", tmp.address));

    if (!fgets (buf, MAXB, fp)) break;
    a3 = ((buf[MAXB - 2] && buf[MAXB - 2] != '\n') ||
          !sscanf (buf, "%31[^\n]", tmp.citystate));

    if (!fgets (buf, MAXB, fp)) break;
    a4 = ((buf[MAXB - 2] && buf[MAXB - 2] != '\n') ||
          !sscanf (buf, "%31[^\n]", tmp.zip));

    if (a1 || a2 || a3 || a4) continue; /* any error, skip all lines */

    if (!(addr[idx] = malloc (sizeof *addr[idx]))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }
    /* copy tmp to addr[idx] and increment index */
    memcpy (addr[idx++], &tmp, sizeof tmp);
}

(注意: buf[MAXB - 2] &amp;&amp; buf[MAXB - 2] != '\n' 只检查 (1) buf 是否已满,以及 (2) nul-terminating 字符之前的字符是否是某物除了'\n' 表示发生短读 并且行中的字符数超过MAXB(您的BUFF_SIZE -- 我不喜欢输入))

至于您的FILE * 指针,您只需要一个fp 指针。 (您一次不会打开多个流)。至于输出函数,如果你考虑一下,你只需要一个输出函数来处理终端(例如stdout文件流)或任何文件(例如fp = fopen (somefile, "w"))的输出。因此,您可以简单地创建一个按您喜欢的格式设置的打印函数。例如:

/** write 'n' addresses to FILE *fp */
void prnaddr (struct info **addr, int n, FILE *fp)
{
    int i;
    for (i = 0; i < n; i++)
        fprintf (fp, " %-8s    %-22s    %-20s    %s\n", addr[i]->name,
                addr[i]->address, addr[i]->citystate, addr[i]->zip);
}

这只是提供了一种合理的单行输出格式,例如:

 K1, K2      720 Eucalyptus Ave 105    Inglewood, CA           89030
 O1, O2      7659 Mckinley Ave         Los Angeles, CA         90001
 G1, G2      20253 Lorenzana Dr        Los Angeles, CA         90005

将所有部分放在一起,并注意当您需要声明多个常量时,您可以简单地使用单个全局 enum 而不是多个 #define 行(并将常量的名称缩短为 MAXC(最多个字符)和MAXS(最大结构),您可以执行类似以下的操作:

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

enum { MAXB = 32, MAXS = 512 };

struct info {
    char name[MAXB];
    char address[MAXB];
    char citystate[MAXB];
    char zip[MAXB];
};

void prnaddr (struct info **addr, int n, FILE *fp);
void selectionSort(struct info *addr[], int size);

int main(int argc, char *argv[]) {

    int i, idx = 0;
    struct info *addr[MAXS] = {NULL};
    FILE *fp = NULL;

    if (argc != 3) {
        printf("Usage: program, inputfile, outputfile\n");
        exit(1);
    }
    if ((fp = fopen(argv[1], "r")) == NULL) {
        printf("Can't open input file\n");
        exit(1);
    }

    for (idx = 0; idx < MAXS;) {
        int a1, a2, a3, a4;
        char buf[MAXB] = "";
        struct info tmp = { .name = "" };

        if (!fgets (buf, MAXB, fp)) break; /* read/validate lines */
        a1 = ((buf[MAXB - 2] && buf[MAXB - 2] != '\n') || 
            !sscanf (buf, "%31[^\n]", tmp.name));

        if (!fgets (buf, MAXB, fp)) break;
        a2 = ((buf[MAXB - 2] && buf[MAXB - 2] != '\n') ||
            !sscanf (buf, "%31[^\n]", tmp.address));

        if (!fgets (buf, MAXB, fp)) break;
        a3 = ((buf[MAXB - 2] && buf[MAXB - 2] != '\n') ||
            !sscanf (buf, "%31[^\n]", tmp.citystate));

        if (!fgets (buf, MAXB, fp)) break;
        a4 = ((buf[MAXB - 2] && buf[MAXB - 2] != '\n') ||
            !sscanf (buf, "%31[^\n]", tmp.zip));

        if (a1 || a2 || a3 || a4) continue; /* any error, skip all lines */

        if (!(addr[idx] = malloc (sizeof *addr[idx]))) {
            fprintf (stderr, "error: virtual memory exhausted.\n");
            return 1;
        }
        /* copy tmp to addr[idx] and increment index */
        memcpy (addr[idx++], &tmp, sizeof tmp);
    }
    fclose(fp);

    selectionSort(addr, idx);

    printf("\nLEAST TO GREATEST\n");
    prnaddr (addr, idx, stdout);

    if ((fp = fopen(argv[2], "w")) == NULL) {
        printf("Can't open output file\n");
        exit(1);
    }
    prnaddr (addr, idx, fp);
    fclose(fp);

    for (i = 0; i < idx; i++) free(addr[i]);

    return 0;
}

/** write 'n' addresses to FILE *fp */
void prnaddr (struct info **addr, int n, FILE *fp)
{
    int i;
    for (i = 0; i < n; i++)
        fprintf (fp, " %-8s    %-22s    %-20s    %s\n", addr[i]->name,
                addr[i]->address, addr[i]->citystate, addr[i]->zip);
}

void selectionSort(struct info *addr[], int size)
{
    int idx1, idx2;
    int minIndex;
    struct info *ptrTemporary;

    for (idx2 = 0; idx2 < size - 1; idx2++)
    {
        minIndex = idx2;
        for (idx1 = idx2 + 1; idx1 < size; idx1++)
        {
            if (strcmp(addr[idx1]->zip, addr[minIndex]->zip) < 0)
                minIndex = idx1;
        }
        if (minIndex != idx2){
            ptrTemporary = addr[idx2];
            addr[idx2] = addr[minIndex];
            addr[minIndex] = ptrTemporary;
        }
    }
}

输入

您提供的输入文件已被使用。

使用/输出示例

$ ./bin/structaddr dat/structaddr.txt dat/structaddrout.txt

LEAST TO GREATEST
 K1, K2      720 Eucalyptus Ave 105    Inglewood, CA           89030
 O1, O2      7659 Mckinley Ave         Los Angeles, CA         90001
 G1, G2      20253 Lorenzana Dr        Los Angeles, CA         90005
 N1, N2      20044 Wells Dr            Beverly Hills, CA       90210
 H1, H2      5241 Del Moreno Dr        Los Angeles, CA         91110
 F1, F2      20225 Lorenzana Dr        Los Angeles, CA         91111
 C1, C2      5142 Dumont Pl            Azusa, CA               91112
 J1, J2      5135 Quakertown Ave       Thousand Oaks, CA       91362
 M1, M2      4819 Quedo Pl             Westlake Village, CA    91362
 E1, E2      4851 Poe Ave              Woodland Hills, CA      91364
 A1, A2      20294 Lorenzana Dr        Woodland Hills, CA      91364
 L1, L2      5021 Dumont Pl            Woodland Hills, CA      91364
 A1, A2      20294 Lorenzana Dr        Woodland Hills, CA      91364
 D1, D2      20636 De Forest St        Woodland Hills, CA      91364
 B1, B2      19831 Henshaw St          Culver City, CA         94023
 I1, I2      5332 Felice Pl            Stevenson Ranch, CA     94135
 I1, I2      5332 Felice Pl            Stevenson Ranch, CA     94135
 I1, I2      5332 Felice Pl            Stevenson Ranch, CA     94135

在文件中输出

$ cat dat/structaddrout.txt
 K1, K2      720 Eucalyptus Ave 105    Inglewood, CA           89030
 O1, O2      7659 Mckinley Ave         Los Angeles, CA         90001
 G1, G2      20253 Lorenzana Dr        Los Angeles, CA         90005
 N1, N2      20044 Wells Dr            Beverly Hills, CA       90210
 H1, H2      5241 Del Moreno Dr        Los Angeles, CA         91110
 F1, F2      20225 Lorenzana Dr        Los Angeles, CA         91111
 C1, C2      5142 Dumont Pl            Azusa, CA               91112
 J1, J2      5135 Quakertown Ave       Thousand Oaks, CA       91362
 M1, M2      4819 Quedo Pl             Westlake Village, CA    91362
 E1, E2      4851 Poe Ave              Woodland Hills, CA      91364
 A1, A2      20294 Lorenzana Dr        Woodland Hills, CA      91364
 L1, L2      5021 Dumont Pl            Woodland Hills, CA      91364
 A1, A2      20294 Lorenzana Dr        Woodland Hills, CA      91364
 D1, D2      20636 De Forest St        Woodland Hills, CA      91364
 B1, B2      19831 Henshaw St          Culver City, CA         94023
 I1, I2      5332 Felice Pl            Stevenson Ranch, CA     94135
 I1, I2      5332 Felice Pl            Stevenson Ranch, CA     94135
 I1, I2      5332 Felice Pl            Stevenson Ranch, CA     94135

查看一下,如果您有任何问题,请告诉我。

【讨论】:

    【解决方案2】:

    首先,我可以看到您正在使用scanf 扫描来自buffer 变量的数据。我至少可以在您的代码中找到以下错误:

    1. 你应该使用fscanf来读取文件fpin

    2. 您的问题还在于scanf (fscanf) 格式规范。

    3. 另外,在 scanf-ing 时,我可以看到您正在将 599 个字符扫描到长度仅为 512 的 buffer 变量中。这具有未定义的行为。

    一种解决方案:您应该一次读取一条记录并逐行读取内容。

    【讨论】:

      猜你喜欢
      • 2011-07-05
      • 2014-12-21
      • 2019-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-31
      相关资源
      最近更新 更多