gpt4 book ai didi

c - 使用 fgets() 和跳行从另一个 CSV 写入 CSV

转载 作者:行者123 更新时间:2023-11-30 16:10:32 29 4
gpt4 key购买 nike

全面披露:学校作业

我一直在编写一些代码来从 CSV 中提取数据并将其移动到另一个 CSV,但是我一直遇到这个似乎无法克服的错误。

对于我正在处理的部分,用户提供带有“DELETE OPT1”的命令行参数,其中 OPT1 是 CSV 中条目的 ID。 deleteStuff() 函数应遍历database.csv 并删除匹配ID 的第一个条目/行。它应该通过创建一个database.tmp,然后将database.csv复制到tmp(不包括第一个匹配条目)来实现这一点。然后删除 csv,然后将 tmp 重命名为database.csv,就好像什么也没发生一样。

但是,源代码 (database.csv) 似乎做错了什么,删除了除 ID 之外的所有内容。下面,我发布了源代码和起始数据库.csv,以及运行 DELETE 10 后代码输出的内容。任何帮助将不胜感激,特别是在理解如何转到 fgets() 的下一行方面。

请注意,noSpaces() 函数只是删除所有空格,因为根据我们的教授,它们可能包含在输入中。

数据库.c:

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

char show[] = "SHOW";
char delete[] = "DELETE";
char add[] = "ADD";

void noSpaces(char* s) {
const char* d = s;
do {
while (*d == ' ') {
++d;
}
} while (*s++ = *d++);
}

void showStuff() {
FILE* csv = fopen("database.csv", "rt");
if (csv == NULL) {
printf("\n File opening failed");
exit(1);
}
char buffer[800];
char *Gptr, *ID, *name, *cAge, *cGPA;
int counter = 1;
while (fgets(buffer, sizeof(buffer), csv)) {
ID = strtok(buffer, ",");
noSpaces(ID);
name = (strtok(NULL, ","));
noSpaces(name);
cAge = (strtok(NULL, ","));
noSpaces(cAge);
int age = atoi(cAge);
cGPA = (strtok(NULL, ","));
noSpaces(cGPA);
double GPA = strtod(cGPA, &Gptr);
printf("Record %d: ID=%-5s NAME:%-5s AGE:%-5d GPA:%.1f\n", counter, ID, name, age, GPA);
counter++;
}
fclose(csv);
}

void deleteStuff(char givenID[]) {
FILE* csvread = fopen("database.csv", "rt");
if (csvread == NULL) {
printf("\n File opening failed");
exit(1);
}
FILE* csvwrite = fopen("database.tmp", "wt");
char buffer[800];
char* ID;
int oneAndDone = 0;
while (fgets(buffer,sizeof(buffer),csvread)) {
ID = strtok(buffer, ",");
noSpaces(ID);
if ((strcmp(ID, givenID) == 0) && (oneAndDone == 0)) {
oneAndDone++;
continue;
}
fprintf(csvwrite, "%s", buffer);
}
system("rm database.csv");
system("mv database.tmp database.csv");
fclose(csvread);
fclose(csvwrite);
if (oneAndDone == 0) {
printf("Sorry, the user was not found. Nothing was deleted.\n\n");
exit(1);
}
}

void addStuff(char gID[], char gName[], char gAge[], char gGPA[]) {
char* Gptr;
int age = atoi(gAge);
double GPA = strtod(gGPA, &Gptr);
FILE* csvappend = fopen("database.csv", "at");
if (csvappend == NULL) {
printf("\n File opening failed");
exit(1);
}
fprintf(csvappend, "%s,%s,%d,%.1f", gID, gName, age, GPA);
fclose(csvappend);
}

void main(int argc, char* argv[]) {
if (argc == 1) {
printf("Your did not provide any arguments. Please enter: ./database CMD OPT1 OPT2 OPT3 OPT4 \n\n");
exit(1);
}
if (strcmp(argv[1], show) == 0) showStuff();
else if (strcmp(argv[1], delete) == 0) {
if (argc <= 2) {
printf("Name of record to delete is missing\n\n");
exit(1);
}
deleteStuff(argv[2]);
}
else if (strcmp(argv[1], add) == 0) {
if (argc <= 5) {
printf("Missing ID, Name, AGE, and GPA Arguments\n\n");
exit(1);
}
addStuff(argv[2], argv[3], argv[4], argv[5]);
}
else printf("The command you requested in invalid. Please select from one of these: SHOW, DELETE, ADD\n\n");

}

原始数据库.csv:

10,bob,18, 3.5
15,mary,20,4.0
5,tom, 17, 3.8

数据库.csv之后:

155

最佳答案

使用 strtok 时,您必须了解它会修改您要标记化的字符串。如果您需要在调用 strtok 后使用该字符串,则应首先复制该字符串。如果您不确定其具体工作原理,请务必检查您正在使用的任何函数的手册页,例如man 3 strtok

   Be cautious when using these functions.  If you do use them, note
that:

* These functions modify their first argument.

除非您正在为没有操作系统(“独立”系统)的其他设备的微 Controller 进行编程,否则使用 void main() 是错误的。请参阅:C11 Standard - §5.1.2.2.1 Program startup(p1) 。另请参阅:What should main() return in C and C++?

您可以使用标志int oneAndDone = 0;——这很好。但是,在找到命令行上提供的 ID 并递增 oneAndDone++; (或仅设置 oneAndDone = 1;)后,会出现根本不再需要调用strtok。在 oneAndDone 不再 0 之后完全跳过 strtok 调用不是更有意义吗?像这样的东西:

    char buffer[800];
char* ID;
int oneAndDone = 0;
while (fgets(buffer,sizeof(buffer),csvread)) {
if (oneAndDone == 0) {
char bcopy[800];
strcpy (bcopy, buffer);
ID = strtok(bcopy, ",");
noSpaces(ID);
if (strcmp(ID, givenID) == 0) {
oneAndDone = 1;
continue;
}
}
fprintf(csvwrite, "%s", buffer);
}

您已经使用 char buffer[800]; 完成了不减少缓冲区大小的出色工作 - 但不要使用Magic-Numbers em> 在你的代码中。更好:

...
#include <string.h>

#define MAXC 800 /* if you need a constant, #define one (or more) */
...
char buffer[MAXC];
char* ID;
int oneAndDone = 0;
while (fgets(buffer,sizeof(buffer),csvread)) {
if (oneAndDone == 0) {
char bcopy[MAXC];
...

在对 rmmv 进行系统调用之前,您当然希望关闭正在读取和写入的文件。例如:

        fclose(csvread);
fclose(csvwrite);
system("rm database.csv");
system("mv database.tmp database.csv");

您总是希望在写入后检查返回 fclose 以捕获仅检查写入本身可能无法捕获的任何流错误或文件问题,例如

         if (fclose(csvwrite) == EOF)
perror ("fclose-csvwrite");

这样您至少可以知道流是否已成功刷新并且关闭时没有错误。

最后,启用编译器警告,并且在没有警告的情况下编译之前不接受代码。对于 gcc/clang,至少使用 -Wall -Wextra -pedantic (也考虑添加 -Wshadow)。对于 VS,请使用 /W3。对于任何其他编译器,请检查文档并启用类似的警告。这将指示您在周围添加括号:

    } while ((*s++ = *d++));

void noSpaces(char* s)中。

这应该能让你继续前进。仔细考虑并进行更改,如果您还有其他问题,请告诉我。

关于c - 使用 fgets() 和跳行从另一个 CSV 写入 CSV,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58869542/

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