使用c

时间:2018-06-28 13:15:23

标签: c

我想阅读我的文档文件夹中的文本文件。我不明白为什么我看不懂它,或者为什么我不能在我的控制台应用程序上写它。当我运行程序时,它只是卡住了。它什么也没做。

我要阅读的文件:

330400199711111890 W1         ZhejiangJianxin                                   
330411193807234897 W2         ZhejiangJianxinÐãÖÞÇø                             
331122199502289716 W3         ZhejiangçÆÔÆÏØ                                    
330402192503284421 M1         ZhejiangJianxinÄϺþÇø                             
330225198403042936 W4         ZhejiangÏóɽÏØ                                    
330681194109099151 W5         ZhejiangÖîôßÊÐ                                    
330727195612078712 W6         ZhejiangÅÍ°²ÏØ                                    
330921193708179044 M2         Zhejiangá·É½ÏØ                                    
330303195103046912 W7         ZhejiangWenzhouÁúÍåÇø                             
330781197108138752 W8         ZhejiangÀ¼ÏªÊÐ                                    
330127193411280584 M3         Zhejiang´¾°²ÏØ                                    
331001193310027792 W9         ZhejiangTaizhouDowntown                           
331125196503132898 W10        ZhejiangÔƺÍÏØ                                    
331000192003056719 W11        ZhejiangTaizhou                                   
330106194503103959 W12        ZhejiangHangzhouWestlakeDistrict                  
330106194610285524 M4         ZhejiangHangzhouWestlakeDistrict                  
330301198301227758 W13        ZhejiangWenzhouDowntown 

程序:

#include <stdio.h>
#include <stdlib.h>
#define NAME_LEN 80

// struct with student information. id, name and address
typedef struct
{
    char id[19];
    char code[20];
    char address[50];
} Student;


int main()
{
    Student newStudent;

    FILE *fp;
    fp=fopen("ID500.txt","r");

    char id[20],code[20],address[50];

    while(!feof(fp))
    {
        fscanf(fp,"%s %s %s",id,code,address);
        printf("%s %s %s\n",id,code,address);
    }
    return 0;
}

2 个答案:

答案 0 :(得分:3)

最好将文件名作为程序的命令行参数提供给程序,因为它更易于测试和使用。

在文件中,每一行似乎是一个单独的记录。因此,最好先阅读每一行,然后解析该行中的字段。

请考虑以下内容:

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

#define  MAX_LINE_LEN  500

int main(int argc, char *argv[])
{
    char  line[MAX_LINE_LEN + 1]; /* +1 for the end-of-string '\0' */
    FILE *in;

    if (argc != 2) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    in = fopen(argv[1], "r");
    if (!in) {
        fprintf(stderr, "Cannot open %s: %s.\n", argv[1], strerror(errno));
        return EXIT_FAILURE;
    }

    while (fgets(line, sizeof line, in) != NULL) {
        char  id[20], code[20], address[50], dummy;

        if (sscanf(line, " %19s %19s %49s %c", id, code, address, &dummy) == 3) {
            /* The line did consist of three fields, and they are
               now correctly parsed to 'id', 'code', and 'address'. */

            printf("id = '%s'\ncode = '%s'\naddress = '%s'\n\n",
                   id, code, address);

        } else {

            /* We do have a line, but it does not consist of
               exactly three fields. */

            /* Remove the newline character(s) at the end of line. */
            line[strcspn(line, "\r\n")] = '\0';

            fprintf(stderr, "Cannot parse line '%s'.\n", line);

        }
    }

    if (ferror(in)) {
        fprintf(stderr, "Error reading %s.\n", argv[1]);
        return EXIT_FAILURE;
    } else
    if (fclose(in)) {
        fprintf(stderr, "Error closing %s.\n", argv[1]);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

以上,argc包含许多命令行参数,程序名用作第一个(零,argv[0])参数。我们需要两个:程序名和要读取的文件名。否则,我们将打印出使用情况消息。

我们尝试打开文件进行读取。如果fopen()失败,则返回NULL,错误存储在errno中。 strerror(errno)产生易于理解的错误消息。

fgets(array, sizeof array, stream)array读取一行(除非太长以至于不能插入stream)。如果成功,它将返回指向array中第一个元素的指针。如果失败(例如,不再需要读取),它将返回NULL

请记住,feof(stream)不会检查stream是否有更多数据要读取。它仅报告stream的结尾是否已经遇到。因此,您应该只读取数据直到读取失败,然后检查读取失败的原因,而不是读取直到feof()返回true为止。上面的示例程序就是这样做的。

我们希望将每一行视为单独的记录。由于fscanf()不能将'\n'与空格区分开(无论是在转换规范中,还是在隐式跳过空格时),因此使用fscanf(in, " %19s %19s %49s", ...)不会将解析限制为一行:它们可能在同一行,或不同行,甚至中间有空行。为了将解析限制为一行,我们首先使用fgets()读取每一行,然后尝试使用sscanf()解析该行,并且仅解析该行。 (sscanf()的工作方式与fscanf()相似,但它的输入来自字符串而不是流。)

为避免buffer overflow,我们必须告诉sscanf()我们的缓冲区可以使用多长时间,切记为字符串结尾标记(NUL,'\0')保留一个字符。由于id的长度为20个字符,因此ID字符串最多可以使用19个字符,因此我们需要使用%19s来正确进行转换。

sscanf()的返回值是成功转换的次数。通过在正常情况下我们预期会失败的末尾添加一个伪字符(%c)转换,我们可以检测行中包含的字符是否超出预期。这就是sscanf()模式进行四次转换的原因,但是如果输入行具有我们期望的格式,我们就要求它们中的前三个必须成功,而第四个(虚拟)必须失败。

请注意,如果我们接受不同格式的输入,则可以尝试几种不同的sscanf()表达式。我喜欢称其为推测性解析。您只需要订购它们,以便首先尝试最复杂的那些,然后接受能够产生预期成功转换次数的第一个。有关实际示例,请查看example C code I used in another answer,以允许用户在命令行上使用name = value对指定模拟详细信息。

line[strcspn(line, "\r\n")] = '\0';表达式确实是一个技巧。 strcspn()是标准的C <string.h>函数,它返回第一个字符串参数中的字符数,直到遇到字符串结尾或第二个字符串中的任何字符,以先发生的为准。因此,strcspn(line, "\r\n")产生line中的字符数,直到遇到字符串'\r''\n'的末尾为止,以先到者为准。我们通过使用其余部分作为行缓冲区的索引来修剪字符串的其余部分,并使字符串在此结束。 (请记住,NUL或'\0'总是以C结尾的字符串。)

while循环之后,我们检查fgets()返回NULL的原因。如果ferror()返回true,则表示存在实际读取错误。这些在当今非常罕见,但是不进行检查就像在没有安全保障的情况下带着武器走动:这是零回报的不必要风险。

在大多数操作系统中,如果您以只读方式打开文件,fclose()甚至不会失败,但是在某些情况下可能会有某些特殊情况。 (此外,当您写入流时,它可能会失败,因为C库可能会缓存数据-将其保存在内部缓冲区中,而不是出于效率考虑而立即将其写入-仅在关闭流时才将其写出。像任何写操作一样,由于真正的写错误,该操作可能会失败;例如,如果存储介质已满。)

但是,只需花费几行C代码即可检查ferror()fclose(),并告知用户。我个人深深地讨厌那些不这样做的程序,因为它们确实冒着在没有警告的情况下静默丢失用户数据的风险。用户可能认为一切都很好,但是下次他们尝试访问其文件时,其中的一些文件便丢失了……他们通常最终都将责任归咎于操作系统,而不是真正的罪魁祸首,错误,恶意的程序,这些程序未能成功执行。向用户警告他们可能已经检测到的错误。

(最好是尽早学习该方法。像安全性一样,错误检查并不是您以后可以真正使用的东西:您要么设计它,要么就不可靠。)

还要注意,Linux man pages project包含一个维护良好的C库函数列表(以及POSIX.1,GNU和Linux特定的函数)。不要被它的名字所迷惑。每个页面都包含一个符合部分,该部分告诉您该页面上描述的功能符合哪些标准。如果是C89,则它几乎可以在您可以想象的所有操作系统中运行。如果它是C99或任何POSIX.1版本,则可能无法在Windows或DOS下运行(或使用古老的Borland C编译器),但可以在大多数其他操作系统上运行。


因为OP显然正在读取非ASCII文件,所以我建议尝试使用该程序的本地化版本,该版本使用宽字符和宽字符串:

#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <wchar.h>
#include <stdio.h>
#include <errno.h>

#define  MAX_WLINE_LEN  500

int main(int argc, char *argv[])
{
    wchar_t  line[MAX_WLINE_LEN + 1]; /* +1 for the end-of-string L'\0' */
    FILE *in;

    if (argc != 2) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    if (setlocale(LC_ALL, "") == NULL)
        fprintf(stderr, "Warning: Your C library does not support your currently set locale.\n");

    if (fwide(stdout, 1) < 1)
        fprintf(stderr, "Warning: Your C library does not support wide standard output.\n");

    in = fopen(argv[1], "r");
    if (!in) {
        fprintf(stderr, "Cannot open %s: %s.\n", argv[1], strerror(errno));
        return EXIT_FAILURE;
    }
    if (fwide(in, 1) < 1)
        fprintf(stderr, "Warning: Your C library does not support wide input from %s.\n", argv[1]);

    while (fgetws(line, sizeof line / sizeof line[0], in) != NULL) {
        wchar_t  id[20], code[20], address[50], dummy;

        if (swscanf(line, L" %19ls %19ls %49ls %lc", id, code, address, &dummy) == 3) {
            /* The line did consist of three fields, and they are
               now correctly parsed to 'id', 'code', and 'address'. */

            wprintf(L"id = '%ls', code = '%ls', address = '%ls'\n",
                   id, code, address);

        } else {

            /* We do have a line, but it does not consist of
               exactly three fields. */

            /* Remove the newline character(s) at the end of line. */
            line[wcscspn(line, L"\r\n")] = L'\0';

            fprintf(stderr, "Cannot parse line '%ls'.\n", line);

        }
    }

    if (ferror(in)) {
        fprintf(stderr, "Error reading %s.\n", argv[1]);
        return EXIT_FAILURE;
    } else
    if (fclose(in)) {
        fprintf(stderr, "Error closing %s.\n", argv[1]);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

上面的代码是纯C99代码,并且应在所有具有符合C99或更高版本的标准C库的操作系统上运行。 (遗憾的是,即使Microsoft对C11进行了“贡献”,微软也不愿实现某些C99功能,这意味着上述代码可能需要其他Windows特定的代码才能在Windows上运行。它在Linux,BSD,和Macs。)

答案 1 :(得分:0)

此错误与您的while(!feof(fp))有关。

按以下说明修改您的while-loop

 while(fscanf(fp,"%s %s %s",id,code,address)==3) // check whether 3 items are read. 
    {
        printf("%s %s %s\n",id,code,address);
    }

这将起作用。您也可以使用EOF

while(fscanf(fp,"%s %s %s",id,code,address)!=EOF)
    {
        printf("%s %s %s\n",id,code,address);
    }

逻辑是在fscanf()函数中,如果发生读取错误或读取时到达文件结尾,'{{1 }}”返回(值小于或什至为零)。