gpt4 book ai didi

c - 使用c从计算机读取和打印文本文件

转载 作者:行者123 更新时间:2023-11-30 20:03:29 25 4
gpt4 key购买 nike

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

我要阅读的文件:

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

最佳答案

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

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

考虑以下:

#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,以允许用户在命令行上使用名称=值对指定模拟详细信息。

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。)

关于c - 使用c从计算机读取和打印文本文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51083902/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com