如何从文件中读取一个长列表的数据?

huangapple go评论121阅读模式
英文:

how do I read a long list of data from a file?

问题

我正在用一个周期表项目练习C语言。我有一个**.txt**文件,其中按照以下顺序包含所有元素的信息:
原子序号 名称 符号 质量数

我的程序应该用数据填充一个结构体数组(以便于搜索)。
问题是我的程序不能一直运行到最后一个元素(原子序号=118),它在54处停止,并且不再打印。它还不会填充数组超过54。

以下是我尝试解决问题的方式

文件:populate.c

  1. #include "periodic_table.h"
  2. int populate(void)
  3. {
  4. struct element
  5. {
  6. int atomic_num;
  7. char name[15];
  8. char symbol[5];
  9. float mass_num;
  10. };
  11. // 用于保存元素的数组
  12. struct element e[118];
  13. // 检查文件是否存在
  14. FILE *ptr = fopen("periodic_table.txt", "r");
  15. if (ptr == NULL)
  16. {
  17. printf("找不到要读取的文件。\n正在退出...\n");
  18. return 1;
  19. }
  20. int line; // txt文件的第一行
  21. for (line = 1; line <= 118; line++)
  22. {
  23. if (fscanf(ptr, "%d %s %s %f", &e
    .atomic_num, e
    .name, e
    .symbol, &e
    .mass_num))
  24. {
  25. // 仅用于调试目的的打印
  26. printf("%d 记录:%d %s %s %.2f\n", line, e
    .atomic_num, e
    .name, e
    .symbol, e
    .mass_num);
  27. }
  28. fflush(stdin);
  29. }
  30. fclose(ptr);
  31. return (0);
  32. }

我的文本文件看起来像这样

  1. 1 Hydrogen H 1.01
  2. 2 Helium He 4.00
  3. 3 Lithium Li 6.94
  4. .
  5. .
  6. .
  7. 116 Livermorium Lv 293.00
  8. 117 Tennesine Ts 294.00
  9. 118 Oganesson Og 294.00
英文:

I am practicing C with a periodic table project. I have this .txt file that contains all the elements in this order:
atomic_number name symbol mass_number.

My program was supposed to populate an array of structures with the data (for easy searching).
The problem is my program doesn't go all the way to the last element (atomic_number = 118) it just stops at 54 and doesn't print further. It also does not populate the array beyond 54.

Here is how I have tried to tackle the problem

file: populate.c

  1. #include &quot;periodic_table.h&quot;
  2. int populate(void)
  3. {
  4. struct element
  5. {
  6. int atomic_num;
  7. char name[15];
  8. char symbol[5];
  9. float mass_num;
  10. };
  11. // Array to hold the elements
  12. struct element e[118];
  13. // checks that the file exists
  14. FILE *ptr = fopen(&quot;periodic_table.txt&quot;, &quot;r&quot;);
  15. if (ptr == NULL)
  16. {
  17. printf(&quot;No file to read from.\nExiting...\n&quot;);
  18. return 1;
  19. }
  20. int line; // first line of the txt file
  21. for (line = 1; line &lt;= 118; line++)
  22. {
  23. if (fscanf(ptr, &quot;%d %s %s %f&quot;, &amp;e
    .atomic_num, &amp;e
    .name, &amp;e
    .symbol, &amp;e
    .mass_num))
  24. {
  25. //print is just for debugging purpose
  26. printf(&quot;%d record: %d %s %s %.2f\n&quot;, line, e
    .atomic_num, e
    .name, e
    .symbol, e
    .mass_num);
  27. }
  28. fflush(stdin);
  29. }
  30. fclose(ptr);
  31. return (0);
  32. }

My text file looks like this

  1. 1 Hydrogen H 1.01
  2. 2 Helium He 4.00
  3. 3 Lithium Li 6.94
  4. .
  5. .
  6. .
  7. 116 Livermorium Lv 293.00
  8. 117 Tennesine Ts 294.00
  9. 118 Oganesson Og 294.00

答案1

得分: 0

你的程序存在多个问题:

  • struct element 和数组 e 的定义仅在 populate 函数内部可见,因此一旦函数返回,数据将不再可用。

  • 索引变量 line 应该从 0117,因为它用于索引数组 e[118]。访问 e[118] 具有未定义的行为。

  • 你应该逐行读取数据并使用 sscanf() 解析每一行,以便更容易进行调试时报告转换错误以及行内容。

  • 测试 if (fscanf(...)) 是不正确的:如果所有 4 次转换都成功,返回值应为 4。任何其他结果都应视为错误。

  • %s 是一种风险较大的格式说明符,因为如果文件内容中的单词超过了预期的长度,fscanf() 可能会导致缓冲区溢出。

  • 不应该对数组作为目标变量使用 &amp;

  • fflush(stdin); 是无用的,并且实际上具有未定义的行为。

以下是修改后的版本:

  1. #include "periodic_table.h"
  2. // 这些定义应该移到 "periodic_table.h"
  3. struct element {
  4. int atomic_num;
  5. char name[15];
  6. char symbol[5];
  7. float mass_num;
  8. };
  9. // 用于保存元素的数组
  10. struct element e[118];
  11. int populate(void) {
  12. // 检查文件是否存在
  13. FILE *ptr = fopen("periodic_table.txt", "r");
  14. if (ptr == NULL) {
  15. fprintf(stderr, "无法读取文件。\n正在退出...\n");
  16. return 1;
  17. }
  18. int line; // 文本文件的第一行
  19. for (line = 0; line < 118; line++) {
  20. char buffer[128];
  21. int pos;
  22. if (!fgets(buffer, sizeof buffer, ptr)) {
  23. fprintf(stderr, "无法读取元素 %d\n", line + 1);
  24. break;
  25. }
  26. if (sscanf(buffer, "%d%14s%4s%f%n", &e
    .atomic_num, e
    .name,
  27. e
    .symbol, &e
    .mass_num, &pos) == 4) {
  28. // 仅用于调试目的的打印
  29. printf("%d 记录: %d %s %s %.2f\n",
  30. line + 1, e
    .atomic_num, e
    .name,
  31. e
    .symbol, e
    .mass_num);
  32. if (buffer[pos] != '\n') {
  33. fprintf(stderr, "第 %d 行: 额外数据: %s\n", line + 1, buffer + pos);
  34. }
  35. } else {
  36. fprintf(stderr, "第 %d 行: 无效数据: %s\n", line + 1, buffer);
  37. }
  38. }
  39. fclose(ptr);
  40. return 0;
  41. }

注意:这只是一个修复了问题的修改版本,你可能需要根据你的具体需求进行进一步的改进和测试。

英文:

There are multiple problems in your program:

  • the definition for struct element and the array e is local to the populate function, so the data will be unavailable once the function returns.

  • the index variable line should run from 0 to 117 as it is used to index the e[118] array. accessing e[118] has undefined behavior.

  • you should read one line at a time and use sscanf() to parse the line, and report conversion errors along with the line contents for easier debugging.

  • the test if (fscanf(...)) is incorrect: the return value should be 4 if all 4 conversions succeeded. any other result should be considered an error.

  • %s is a risky conversion specifier as fscanf() can cause a buffer overflow if the file contents has words longer than expected.

  • you should not use &amp; for destination variables that are arrays.

  • fflush(stdin); is useless and actually has undefined behavior.

Here is a modified version:

  1. #include &quot;periodic_table.h&quot;
  2. // these definitions should be moved to &quot;periodic_table.h&quot;
  3. struct element {
  4. int atomic_num;
  5. char name[15];
  6. char symbol[5];
  7. float mass_num;
  8. };
  9. // Array to hold the elements
  10. struct element e[118];
  11. int populate(void) {
  12. // checks that the file exists
  13. FILE *ptr = fopen(&quot;periodic_table.txt&quot;, &quot;r&quot;);
  14. if (ptr == NULL) {
  15. fprintf(stderr, &quot;No file to read from.\nExiting...\n&quot;);
  16. return 1;
  17. }
  18. int line; // first line of the txt file
  19. for (line = 0; line &lt; 118; line++) {
  20. char buffer[128];
  21. int pos;
  22. if (!fgets(buffer, sizeof buffer, ptr)) {
  23. fprintf(stderr, &quot;cannot read element %d\n&quot;, line + 1);
  24. break;
  25. }
  26. if (sscanf(buffer, &quot;%d%14s%4s%f%n&quot;, &amp;e
    .atomic_num, e
    .name,
  27. e
    .symbol, &amp;e
    .mass_num, &amp;pos) == 4) {
  28. //print is just for debugging purpose
  29. printf(&quot;%d record: %d %s %s %.2f\n&quot;,
  30. line + 1, e
    .atomic_num, e
    .name,
  31. e
    .symbol, e
    .mass_num);
  32. if (buffer[pos] != &#39;\n&#39;) {
  33. fprintf(stderr, &quot;line %d: extra data: %s\n&quot;, line + 1, buffer + pos);
  34. }
  35. } else {
  36. fprintf(stderr, &quot;line %d: invalid data: %s\n&quot;, line + 1, buffer);
  37. }
  38. }
  39. fclose(ptr);
  40. return 0;
  41. }

huangapple
  • 本文由 发表于 2023年7月23日 15:55:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76747182.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定