我想读取我的文档文件夹中的文本文件。我不明白为什么我不能读取它,或者如果它被读取,为什么我不能在我的控制台应用程序上写它。当我运行程序时,它只是卡住了。它什么都不做。
我想阅读的文件:
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'
空格(在转换规范中,也不在隐式跳过空格时),所以 usingfscanf(in, " %19s %19s %49s", ...)
不会将解析限制为单行:它们可能在同一行,也可能在不同的行,甚至在之间。为了将我们的解析限制为一行,我们首先使用 读取每一行fgets()
,然后尝试解析该行,并且仅使用sscanf()
. (sscanf()
工作方式与 类似fscanf()
,但从字符串而不是流中获取输入。)
为了避免缓冲区溢出,我们必须告诉sscanf()
我们的缓冲区可以有多长,记住为字符串结束标记 (NUL, '\0'
)保留一个字符。因为id
是 20 个字符,我们最多可以使用 19 个字符作为 ID 字符串,因此我们需要使用它%19s
来正确地进行转换。
from 的返回值sscanf()
是成功转换的次数。通过%c
在我们预计在正常情况下会失败的末尾添加一个虚拟字符 ( ) 转换,我们可以检测该行是否包含超过我们预期的内容。这就是为什么该sscanf()
模式有四次转换,但如果输入行具有我们预期的格式,我们需要前三个转换成功,第四个,虚拟转换失败。
请注意sscanf()
,如果我们接受不同格式的输入,我们可以尝试几种不同的表达式。我喜欢称之为推测性解析。您只需要对它们进行排序,以便您首先尝试最复杂的那些,然后接受产生预期成功转换次数的第一个。有关这方面的实际示例,请查看我在另一个答案中使用的示例 C 代码,以允许用户在命令行上使用名称=值对指定模拟详细信息。
该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 库可能会缓存数据——将其保存在内部缓冲区中,而不是立即写入,以提高效率——并且仅在您关闭流时才将其写出。像任何写入一样,这可能由于真正的写入错误而失败;例如,如果存储介质已经满了。)
然而,检查ferror()
和fclose()
并让用户知道只需要几行 C 代码。我个人非常讨厌不这样做的程序,因为它们确实有可能在没有警告的情况下悄悄丢失用户数据。用户可能认为一切都很好,但是下次他们尝试访问他们的文件时,其中一些文件丢失了……他们通常最终归咎于操作系统,而不是真正的罪魁祸首,是那些失败的坏程序警告用户他们可能检测到的错误。
(最好尽早学会这样做。就像安全性一样,错误检查不是你以后可以真正关注的:你要么设计它,要么它就不可靠。)
另请注意,Linux 手册页项目包含维护良好的 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 库的操作系统。(不幸的是,微软不愿意实现一些 C99 特性,即使它“贡献”到 C11,这意味着上面的代码可能需要额外的特定于 Windows 的代码才能在 Windows 上运行。它在 Linux、BSD、和 Mac,但是。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句