英文:
Problems with fscanf skipping lines when assigning strings to a linked list from a txt file in C
问题
以下是您提供的代码的翻译部分:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_REG 8+1
#define MAX_OSG 2+1
#define MAX_DIO 5+1
typedef struct automobili {
char registracija[MAX_REG];
char osiguran[MAX_OSG];
char dio[MAX_DIO];
double steta;
struct automobili *sledeci;
} AUTO;
// 打开文本文件
FILE *open(char *ime, char *tip) {
FILE *fp = fopen(ime, tip);
if (fp == NULL) {
if (strcmp(tip, "r") == 0) {
printf("Nije moguće otvoriti ulaznu datoteku %s!\n", ime);
exit(1);
} else if (strcmp(tip, "w") == 0) {
printf("Nije moguće otvoriti izlaznu datoteku %s!\n", ime);
exit(2);
}
}
return fp;
}
// 创建新的链表元素
AUTO *novi_cvor(char registracija[], char osiguran[], char dio[], double steta) {
AUTO *novi = (AUTO *)malloc(sizeof(AUTO));
if (novi == NULL) {
printf("Greška prilikom zauzimanja memorije!\n");
exit(EXIT_FAILURE);
}
strcpy(novi->registracija, registracija);
strcpy(novi->osiguran, osiguran);
strcpy(novi->dio, dio);
novi->steta = steta;
novi->sledeci = NULL;
return novi;
}
// 添加元素到链表的末尾
void dodaj_cvor(AUTO **glava, AUTO *novi) {
if (*glava == NULL) {
*glava = novi;
return;
} else {
AUTO *tekuci = *glava;
while (tekuci->sledeci != NULL) {
tekuci = tekuci->sledeci;
}
tekuci->sledeci = novi;
}
}
// 从输入文件读取并使用函数将新元素添加到链表中
void unos(FILE *in, AUTO **glava) {
char registracija[MAX_REG];
char osiguran[MAX_OSG];
char dio[MAX_DIO];
double steta;
while (fscanf(in, "%s %s %s %lf", registracija, osiguran, dio, &steta) != EOF) {
AUTO *novi = novi_cvor(registracija, osiguran, dio, steta);
dodaj_cvor(glava, novi);
}
}
void inicijalizacija(AUTO **glava) {
*glava = NULL;
}
// 删除链表
void brisanje(AUTO **glava) {
if (*glava != NULL) {
brisanje(&((*glava)->sledeci));
free(*glava);
*glava = NULL;
}
}
void provjera_osiguranja(AUTO *glava) {
while (glava != NULL) {
if (strcmp(glava->osiguran, "da") == 0) {
glava->steta = glava->steta * 0.85;
}
glava = glava->sledeci;
}
}
// 检查正确的元素并将它们写入输出文件
void provjera_registracije(FILE *out, AUTO *glava, char br_reg[]) {
double sum = 0;
while (glava != NULL) {
if (strcmp(glava->registracija, br_reg) == 0) {
fprintf(out, "%s %s %s %.2lf\n", glava->registracija, glava->osiguran, glava->dio, glava->steta);
sum += glava->steta;
}
glava = glava->sledeci;
}
if (sum == 0) {
printf("Registracija nije na spisku!\n");
exit(11);
}
fprintf(out, "Ukupna vrijednost štete je %.2lf din.\n", sum);
}
int main(int argc, char **argv) {
char br_reg[MAX_REG];
// 期望的输入格式为 "./a.out automobili.txt <exit_filename>.txt"
if (argc != 3) {
printf("Loš unos! Pokušajte ponovo!\n");
exit(EXIT_FAILURE);
}
FILE *in = open(argv[1], "r");
FILE *out = open(argv[2], "w");
AUTO *glava;
inicijalizacija(&glava);
unos(in, &glava);
printf("Unesite registraciju: ");
fgets(br_reg, MAX_REG, stdin);
provjera_osiguranja(glava);
provjera_registracije(out, glava, br_reg);
brisanje(&glava);
fclose(in);
fclose(out);
return 0;
}
请注意,此代码翻译部分仅包括代码本身的注释和字符串,不包括任何其他内容。希望这可以帮助您解决代码问题。如果您有其他问题,请随时提出。
英文:
The following code reads the input from a file called "automobili.txt" and outputs them into a different file, however I cannot figure out why the code skips over some strings when scanning the input
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_REG 8+1
#define MAX_OSG 2+1
#define MAX_DIO 5+1
typedef struct automobili {
char registracija[MAX_REG];
char osiguran[MAX_OSG];
char dio[MAX_DIO];
double steta;
struct automobili *sledeci;
} AUTO;
//opens the txt files
FILE *open(char *ime, char *tip){
FILE *fp = fopen(ime, tip);
if (fp == NULL) {
if (strcmp(tip, "r") == 0) {
printf("Nije moguće otvoriti ulaznu datoteku %s!\n", ime);
exit(1);
} else if (strcmp(tip, "w") == 0) {
printf("Nije moguće otvoriti izlaznu datoteku %s!\n", ime);
exit(2);
}
}
return fp;
}
//creates new list element
AUTO *novi_cvor(char registracija[], char osiguran[], char dio[], double steta){
AUTO *novi = (AUTO*)malloc(sizeof(AUTO));
if (novi == NULL) {
printf("Greška prilikom zauzimanja memorije!\n");
exit(EXIT_FAILURE);
}
strcpy(novi->registracija, registracija);
strcpy(novi->osiguran, osiguran);
strcpy(novi->dio, dio);
novi->steta = steta;
novi->sledeci = NULL;
return novi;
}
//adds element to the end of the linked list
void dodaj_cvor(AUTO **glava, AUTO *novi){
if (*glava == NULL) {
*glava = novi;
return;
} else {
AUTO *tekuci = *glava;
while (tekuci->sledeci != NULL) {
tekuci = tekuci->sledeci;
}
tekuci->sledeci = novi;
}
}
//reads from the input file and adds new elements to the list using functions
void unos(FILE *in, AUTO **glava){
char registracija[MAX_REG];
char osiguran[MAX_OSG];
char dio[MAX_DIO];
double steta;
while (fscanf(in, "%s %s %s %lf", registracija, osiguran, dio, &steta) != EOF) {
AUTO *novi = novi_cvor(registracija, osiguran, dio, steta);
dodaj_cvor(glava, novi);
}
}
void inicijalizacija(AUTO **glava){
*glava = NULL;
}
//deletes the list
void brisanje(AUTO **glava){
if (*glava != NULL) {
brisanje(&((*glava)->sledeci));
free(*glava);
*glava = NULL;
}
}
void provjera_osiguranja(AUTO *glava) {
while (glava != NULL) {
if (strcmp(glava->osiguran, "da") == 0) {
glava->steta = glava->steta * 0.85;
}
glava = glava->sledeci;
}
}
//checks for the right elemets and writres them down in the output file
void provjera_registracije(FILE *out, AUTO *glava, char br_reg[]){
double sum=0;
while (glava != NULL) {
if (strcmp(glava->registracija, br_reg) == 0) {
fprintf(out, "%s %s %s %.2lf\n", glava->registracija, glava->osiguran, glava->dio, glava->steta);
sum += glava->steta;
}
glava = glava->sledeci;
}
if (sum == 0) {
printf("Registracija nije na spisku!\n");
exit(11);
}
fprintf(out, "Ukupna vrijednost štete je %.2lf din.\n", sum);
}
int main(int argc, char **argv){
char br_reg[MAX_REG];
//expected input is "./a.out automobili.txt <exit_filename>.txt"
if (argc != 3) {
printf("Loš unos! Pokušajte ponovo!\n");
exit(EXIT_FAILURE);
}
FILE *in = open(argv[1], "r");
FILE *out = open(argv[2], "w");
AUTO *glava;
inicijalizacija(&glava);
unos(in, &glava);
printf("Unesite registraciju: ");
fgets(br_reg, MAX_REG, stdin);
provjera_osiguranja(glava);
provjera_registracije(out, glava, br_reg);
brisanje(&glava);
fclose(in);
fclose(out);
return 0;
}
The input file:
PA454-TS da menjac 5500
BG777-OS da volan 11500
BG777-OS ne branik 7000
BG777-OS ne menjac 23500
PA454-TS da gume 16000
BG777-OS da kvacilo 33000
P.S. I apologize for non English code, the important functions have been commented on what each of them does.
The expected output when not checking for a specific registration number should have been:
PA454-TS da menjac 4675.00
BG777-OS da volan 9775.00
BG777-OS ne branik 7000.00
BG777-OS ne menjac 23500.00
PA454-TS da gume 13600.00
BG777-OS da kvacilo 28050.00
Ukupna vrijednost štete je 86600.00 din.
However the program outputs:
da menjac 4675.00
BG777-OS da volan 9775.00
ne branik 7000.00
ne menjac 23500.00
PA454-TS da gume 13600.00
o da kvacilo 28050.00
Ukupna vrijednost štete je 86600.00 din.
I have tried rewriting the code however nothing changed and I cannot find a difference in how I executed the input here as compared to my previous code which works fine. I suspect the issue comes from fscanf() and not assigning the string to the element itself.
答案1
得分: 1
以下是已翻译的代码部分:
有多个问题在代码中:
`while (fscanf(in, "%s %s %s %lf", registracija, osiguran, dio, &steta) != EOF)` 很容易出错且不安全:
* 如果任何字段在 `%s` 转换中的长度超过了相应目标数组的长度,将导致缓冲区溢出和未定义行为。
* 事实上,数组 `dio` 的长度为 `5+1` 字节,而输入文件中的每一行的这个字段都有更长的字符串,这将导致未定义行为。你应该在格式字符串中指定最大长度,如 `%8s %2s %5s %lf`,并可能增加这些长度以适应实际的文件内容。
* 比较 `fscanf()` 的返回值与 `EOF` 只会检测在任何字段可以转换之前出现的意外文件结束。相反,你应该测试返回值是否为 `4`,表示所有 4 个转换已成功。
* 直接调用 `fscanf()` 使得在输入文件中检测无效行变得困难。建议使用 `fgets()` 从文件中读取一行,并使用 `sscanf()` 尝试转换,在失败时生成有信息的错误消息。
* `open` 作为函数或变量名应该避免使用,因为这个名称在 POSIX 系统上引用了一个系统调用。使用 `open_file` 既更安全又更可读。
* 还要注意,宏定义,如 `#define MAX_REG 8+1` 是有风险的:如果你在代码中写 `MAX_REG*2`,它将展开为 `8+1*2`,其值为 `10`,而不是预期的 `18`。始终要给宏表达式以及宏表达式中使用的宏参数加上括号。
以下是修改后的版本:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_REG (8+1)
#define MAX_OSG (2+1)
#define MAX_DIO (8+1)
typedef struct automobili {
char registracija[MAX_REG];
char osiguran[MAX_OSG];
char dio[MAX_DIO];
double steta;
struct automobili *sledeci;
} AUTO;
// 打开文本文件
FILE *open_file(const char *ime, const char *tip) {
FILE *fp = fopen(ime, tip);
if (fp == NULL) {
if (strcmp(tip, "r") == 0) {
printf("无法打开输入文件 %s!\n", ime);
exit(1);
} else if (strcmp(tip, "w") == 0) {
printf("无法打开输出文件 %s!\n", ime);
exit(2);
} else {
printf("无法以模式 %s 打开文件 %s!\n", tip, ime);
exit(3);
}
}
return fp;
}
// 如果没有截断,则返回字符串的长度。
// 否则截断字符串并返回目标大小。
// 如果 size > 0,则目标始终以 null 结尾
size_t pstrcpy(char *dest, size_t size, const char *src) {
size_t len;
for (len = 0; len < size && src[len]; len++)
continue;
if (len < size)
size = len + 1;
if (size > 0) {
memmove(dest, src, size - 1);
dest[size - 1] = '\0';
}
return len;
}
// 创建新的列表元素
AUTO *novi_cvor(char registracija[], char osiguran[], char dio[], double steta) {
AUTO *novi = (AUTO *)malloc(sizeof(AUTO));
if (novi == NULL) {
printf("内存分配失败!\n");
exit(EXIT_FAILURE);
}
pstrcpy(novi->registracija, sizeof novi->registracija, registracija);
pstrcpy(novi->osiguran, sizeof novi->osiguran, osiguran);
pstrcpy(novi->dio, sizeof novi->dio, dio);
novi->steta = steta;
novi->sledeci = NULL;
return novi;
}
// 在链表末尾添加元素
void dodaj_cvor(AUTO **glava, AUTO *novi) {
if (*glava == NULL) {
*glava = novi;
return;
} else {
AUTO *tekuci = *glava;
while (tekuci->sledeci != NULL) {
tekuci = tekuci->sledeci;
}
tekuci->sledeci = novi;
}
}
// 从输入文件读取并使用函数将新元素添加到列表中
void unos(FILE *in, AUTO **glava) {
char registracija[MAX_REG];
char osiguran[MAX_OSG];
char dio[MAX_DIO];
double steta;
char buf[100];
char c;
while (fgets(buf, sizeof buf, in)) {
if (sscanf(buf, "%8s %2s %8s %lf %c", registracija, osiguran, dio, &steta, &c) == 4) {
AUTO *novi = novi_cvor(registracija, osiguran, dio, steta);
dodaj_cvor(glava, novi);
} else {
printf("无效的输入行:%s\n", buf);
}
}
}
void inicijalizacija(AUTO **glava) {
*glava = NULL;
}
// 删除列表
void brisanje(AUTO **glava) {
while (*glava != NULL) {
AUTO *p = *glava;
*glava = p->sledeci;
free(p);
}
}
void provjera_osiguranja(AUTO *glava) {
while (glava != NULL) {
if (strcmp(glava->osiguran, "da") == 0) {
glava->steta = glava->steta * 0.85;
}
glava = glava->sledeci;
}
}
// 检查正确的元素并将其写入输出文件
void provjera_registracije(FILE *out, AUTO *glava, char br_reg[]) {
double sum = 0;
<details>
<summary>英文:</summary>
There are multiple problems in the code:
`while (fscanf(in, "%s %s %s %lf", registracija, osiguran, dio, &steta) != EOF)` is very brittle and unsafe:
* if any of the fields parsed for the `%s` conversions exceeds the length of the corresponding target array, this will cause a buffer overflow with undefined behavior.
* as a matter of fact, the array `dio` has a length of `5+1` bytes and every other line in the input file has a longer string for this field, causing undefined behavior. You should specify the maximum length in the format string as `%8s %2s %5s %lf` and should probably increase these lengths to accommodate the actual file contents.
* comparing the return value of `fscanf()` to `EOF` will only detect an unexpected end of file occurring before any field can be converted. You should instead test that the return value is `4`, signifying all 4 conversions have succeeded.
* calling `fscanf()` directly makes it difficult to detect invalid lines in the input file. It is recommended to use `fgets()` to read a line from the file and `sscanf()` to attempt the conversion, producing an informative error message upon failure.
* `open` as a function or variable name should be avoided as this name refers to a system call on POSIX systems. Using `open_file` is both safer and more readable.
* also note that macro definitions such as `#define MAX_REG 8+1` are risky: if you write `MAX_REG*2` in the code, it will expand to `8+1*2` which has the value `10` instead of the expected `18`. Always parenthesize macro expressions as well as macro arguments used in the macro expression.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_REG (8+1)
#define MAX_OSG (2+1)
#define MAX_DIO (8+1)
typedef struct automobili {
char registracija[MAX_REG];
char osiguran[MAX_OSG];
char dio[MAX_DIO];
double steta;
struct automobili *sledeci;
} AUTO;
//opens the txt files
FILE *open_file(const char *ime, const char *tip) {
FILE *fp = fopen(ime, tip);
if (fp == NULL) {
if (strcmp(tip, "r") == 0) {
printf("Nije moguće otvoriti ulaznu datoteku %s!\n", ime);
exit(1);
} else
if (strcmp(tip, "w") == 0) {
printf("Nije moguće otvoriti izlaznu datoteku %s!\n", ime);
exit(2);
} else {
printf("Nije moguće otvoriti datoteku %s za mod %s!\n", ime, tip);
exit(3);
}
}
return fp;
}
// returns the length of the string if no truncation.
// otherwise truncate the string and return the destination size.
// destination is always null terminated if size > 0
size_t pstrcpy(char *dest, size_t size, const char *src) {
//size_t len = strnlen(src, size);
size_t len;
for (len = 0; len < size && src[len]; len++)
continue;
if (len < size)
size = len + 1;
if (size > 0) {
memmove(dest, src, size - 1);
dest[size - 1] = '\0';
}
return len;
}
//creates new list element
AUTO *novi_cvor(char registracija[], char osiguran[], char dio[], double steta) {
AUTO novi = (AUTO)malloc(sizeof(AUTO));
if (novi == NULL) {
printf("Greška prilikom zauzimanja memorije!\n");
exit(EXIT_FAILURE);
}
pstrcpy(novi->registracija, sizeof novi->registracija, registracija);
pstrcpy(novi->osiguran, sizeof novi->osiguran, osiguran);
pstrcpy(novi->dio, sizeof novi->dio, dio);
novi->steta = steta;
novi->sledeci = NULL;
return novi;
}
//adds element to the end of the linked list
void dodaj_cvor(AUTO **glava, AUTO *novi) {
if (*glava == NULL) {
*glava = novi;
return;
} else {
AUTO *tekuci = *glava;
while (tekuci->sledeci != NULL) {
tekuci = tekuci->sledeci;
}
tekuci->sledeci = novi;
}
}
//reads from the input file and adds new elements to the list using functions
void unos(FILE *in, AUTO **glava) {
char registracija[MAX_REG];
char osiguran[MAX_OSG];
char dio[MAX_DIO];
double steta;
char buf[100];
char c;
while (fgets(buf, sizeof buf, in)) {
if (sscanf(buf, "%8s %2s %8s %lf %c", registracija, osiguran, dio, &steta, &c) == 4) {
AUTO *novi = novi_cvor(registracija, osiguran, dio, steta);
dodaj_cvor(glava, novi);
} else {
printf("invalid input line: %s\n", buf);
}
}
}
void inicijalizacija(AUTO **glava) {
*glava = NULL;
}
//deletes the list
void brisanje(AUTO **glava) {
while (*glava != NULL) {
AUTO *p = *glava;
*glava = p->sledeci;
free(p);
}
}
void provjera_osiguranja(AUTO *glava) {
while (glava != NULL) {
if (strcmp(glava->osiguran, "da") == 0) {
glava->steta = glava->steta * 0.85;
}
glava = glava->sledeci;
}
}
//checks for the right elemets and writres them down in the output file
void provjera_registracije(FILE *out, AUTO *glava, char br_reg[]) {
double sum = 0;
while (glava != NULL) {
if (strcmp(glava->registracija, br_reg) == 0) {
fprintf(out, "%s %s %s %.2lf\n", glava->registracija, glava->osiguran, glava->dio, glava->steta);
sum += glava->steta;
}
glava = glava->sledeci;
}
if (sum == 0) {
printf("Registracija nije na spisku!\n");
exit(11);
}
fprintf(out, "Ukupna vrijednost štete je %.2lf din.\n", sum);
}
int main(int argc, char **argv) {
char br_reg[MAX_REG];
//expected input is "./a.out automobili.txt <exit_filename>.txt"
if (argc != 3) {
printf("Loš unos! Pokušajte ponovo!\n");
exit(EXIT_FAILURE);
}
FILE *in = open_file(argv[1], "r");
FILE *out = open_file(argv[2], "w");
AUTO *glava;
inicijalizacija(&glava);
unos(in, &glava);
printf("Unesite registraciju: ");
fgets(br_reg, MAX_REG, stdin);
provjera_osiguranja(glava);
provjera_registracije(out, glava, br_reg);
brisanje(&glava);
fclose(in);
fclose(out);
return 0;
}
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论