英文:
Two lines in the output with special charachters that shouldn't be there
问题
以下是您提供的文本的翻译:
我有一个以这种格式列出歌曲的txt文件:
we will rock you
queen
2:01
我需要将这些数据放入名为"elenco"的列表中,并使用以下结构:
struct elenco{
char titolo[30];
char autore[30];
int durata_in_sec;
int rips;
struct elenco *prossima;
};
我尝试使用fscanf(fp, "%29[^\n]%*c", elenco->autore);(以此类推)存储数据,并使用简单的printf("%s\n", elenco->autore);输出,但输出正确,直到程序输出两行特殊字符,如下所示:
> ?w
> ☻ ☻ ☻ ☻ ☻ ☻h☻(☻(☻(☻(☻ ☻ ☻ ☻ ☻y
如果您想检查它,这是完整的程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct elenco{
char titolo[30];
char autore[30];
int durata_in_sec;
int rips;
struct elenco *prossima;
};
int main()
{
FILE *fp;
fp = fopen("musica.txt", "r");
int i=0;
int secondi, minuti;
struct elenco *start = NULL;
struct elenco *elenco = NULL;
struct elenco *prec = NULL;
while (!feof(fp))
{
elenco = malloc(sizeof(struct elenco));
fscanf(fp, "%29[^\n]%*c", elenco->titolo);
fscanf(fp, "%29[^\n]%*c", elenco->autore);
fscanf(fp, "%d:%d", &minuti, &secondi);
elenco->durata_in_sec = minuti*60+secondi;
elenco->rips=1;
elenco->prossima = NULL;
if(i==0)
{
start = elenco;
}
else
{
prec->prossima = elenco;
}
prec = elenco;
i++;
}
// 打印
elenco = start;
while(elenco!=NULL)
{
printf("%s\n", elenco->titolo);
printf("%s\n", elenco->autore);
printf("%d\n", elenco->durata_in_sec);
printf("%d\n", elenco->rips);
elenco = elenco->prossima;
}
fclose(fp);
// 释放内存
elenco = start;
while (elenco != NULL) {
struct elenco* temp = elenco;
elenco = elenco->prossima;
free(temp);
}
return 0;
}
这是txt文件的内容:
we will rock you
queen
2:01
it's my life
bon jovi
3:46
we will rock you
queen
2:01
the show must go on
queen
4:36
<details>
<summary>英文:</summary>
I have a txt with a list of songs in this format:
we will rock you
queen
2:01
and i need to put those data in a list named "elenco" with this struct:
struct elenco{
char titolo[30];
char autore[30];
int durata_in_sec;
int rips;
struct elenco *prossima;
};
I tried to store the datas with `fscanf(fp, "%29[^\n]%*c", elenco->autore);` (and so on) and the output with a simple `printf("%s\n", elenco->autore);` but the output is correct until the program outputs two lines with special characters like this:
> ?w
> ☻ ☻ ☻ ☻ ☻ ☻h☻(☻(☻(☻(☻ ☻ ☻ ☻ ☻y
this is the full program if you want to check it
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct elenco{
char titolo[30];
char autore[30];
int durata_in_sec;
int rips;
struct elenco *prossima;
};
int main()
{
FILE *fp;
fp = fopen("musica.txt", "r");
int i=0;
int secondi, minuti;
struct elenco *start = NULL;
struct elenco *elenco = NULL;
struct elenco *prec = NULL;
while (!feof(fp))
{
elenco = malloc(sizeof(struct elenco));
fscanf(fp, "%29[^\n]%*c", elenco->titolo);
fscanf(fp, "%29[^\n]%c", elenco->autore);
fscanf(fp, "%d:%d", &minuti, &secondi);
elenco->durata_in_sec = minuti60+secondi;
elenco->rips=1;
elenco->prossima = NULL;
if(i==0)
{
start = elenco;
}
else
{
prec->prossima = elenco;
}
prec = elenco;
i++;
}
// Stampa
elenco = start;
while(elenco!=NULL)
{
printf("%s\n", elenco->titolo);
printf("%s\n", elenco->autore);
printf("%d\n", elenco->durata_in_sec);
printf("%d\n", elenco->rips);
elenco = elenco->prossima;
}
fclose(fp);
// Free memory
elenco = start;
while (elenco != NULL) {
struct elenco* temp = elenco;
elenco = elenco->prossima;
free(temp);
}
return 0;
}
and this is the txt
we will rock you
queen
2:01
it's my life
bon jovi
3:46
we will rock you
queen
2:01
the show must go on
queen
4:36
</details>
# 答案1
**得分**: 3
1. 使用符号常量代替魔法数字。
2. 更喜欢初始化变量。
3. 删除未使用的变量。 `i` 仅用于确定 `start` 是否已设置,因此可以将其删除。
4. 检查 `fopen()` 的返回值。
5. 使用 `str()` 宏让编译器生成最大字段宽度。
6. `" %[^\n]"` 通常比 `"%[^\n]%*c"` 更可读。前者忽略前导空格,而后者忽略最后一个字符。
7. `while(!feof())` 无法正常工作。在这种情况下,您可以使用单个 `fscanf()` 调用读取整个记录。这还可以让您在一个地方检查无效输入/`EOF`。
8. 检查 `malloc()` 的返回值。
9. 随着每个记录的预分配,您需要释放上一个记录。
10. 确保您的字符串以 '\0' 结尾。
11. 在完成后立即使用 `fclose()` 关闭文件。
12. 限制变量的作用域。这会使您的代码更容易理解。
13. 更喜欢使用 `for` 循环进行迭代。
14. (未修复)考虑将代码拆分为较小的函数(`elenco_load()`、`elenco_print()`、`elenco_free()`)。这会使代码更容易理解和测试。
15. (未修复)考虑使用更具体的数据类型。您是否允许负的 `durata_in_sec`?您是否期望 `INT_MAX` 的持续时间,还是较小的数据类型足够?`secondi < 0 || secondi >= 60` 是否是有效输入(`unsigned char`)?
16. (未修复)由于您硬编码了 `rips=1`,考虑在 `struct elenco` 中将其替换为符号常量 `#define RIPS 1`。这样在打印时您只需执行 `print("%d\n", RIPS)`。
请注意,这些是关于代码的建议和评论,而不是直接的翻译。
<details>
<summary>英文:</summary>
1. Use symbolic constants instead of magic numbers.
1. Prefer initialized variables.
1. Remove unused variables. `i` is only used to determine if `start` has been set so might as well eliminate it, too.
1. Check return value from `fopen()`.
1. Use the `str()` macro to let the compiler generate your maximum field width.
1. " %[^\n]" is usually more readable than "%[^\n]%*c". The former ignores leading white space, while the latter ignores exactly one trailing character.
1. `while(!feof())` doesn't work. In this case you can read the whole record with a single `fscanf()` call. This also gives you one place to check for invalid input / `EOF`.
1. Check return value from `malloc()`.
1. As you pre-allocate each record you need to free the last one.
1. Ensure your strings are '\0' terminated.
1. `fclose()` file as soon as you are done.
1. Minimize scope of variables. This makes your code easier to reason about.
1. Prefer `for`-loops for iteration.
1. (Not fixed) Consider breaking your code into smaller functions (`elenco_load()`, `elenco_print()`, `elenco_free()`). It makes it easier to reason about and test.
1. (Not fixed) Consider using more specific types. Do you want to allow negative `durata_in_sec`? Do you expect duration of `INT_MAX` or would a smaller type do? Is `secondi < 0 || secondi >= 60` valid input (`unsigned char`)?
1. (Not fixed) As you hard-code `rips=1` consider eliminating it from `struct elenco` in favor of a symbolic constant `#define RIPS 1`. When printing you now just do `print("%d\n", RIPS).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TITOLO_LEN 29
#define AUTORE_LEN 29
#define str(s) str2(s)
#define str2(s) #s
struct elenco {
char titolo[TITOLO_LEN+1];
char autore[AUTORE_LEN+1];
int durata_in_sec;
int rips;
struct elenco *prossima;
};
int main() {
FILE *fp = fopen("musica.txt", "r");
if(!fp) {
printf("fopen failed\n");
return 1;
}
struct elenco *start = NULL;
for(struct elenco *prec = NULL;;) {
struct elenco *elenco = malloc(sizeof elenco);
if(!elenco) {
printf("malloc failed\n");
return 1;
}
int secondi, minuti;
int rv = fscanf(fp,
" %" str(TITOLO_LEN) "[^\n]"
" %" str(AUTORE_LEN) "[^\n]"
" %d:%d",
elenco->titolo,
elenco->autore,
&minuti,
&secondi
);
if(rv != 4) {
free(elenco);
if(prec)
prec->prossima = NULL;
break;
}
elenco->durata_in_sec = minuti60+secondi;
elenco->rips=1;
if(!start)
start = elenco;
else
prec->prossima = elenco;
prec = elenco;
}
fclose(fp);
// Stampa
for(struct elenco *elenco = start; elenco; elenco = elenco->prossima) {
printf("%s\n", elenco->titolo);
printf("%s\n", elenco->autore);
printf("%d\n", elenco->durata_in_sec);
printf("%d\n", elenco->rips);
}
// Free memory
for(struct elenco *elenco = start; elenco;) {
struct elenco* temp = elenco;
elenco = elenco->prossima;
free(temp);
}
return 0;
}
and example run:
we will rock you
queen
121
1
it's my life
bon jovi
226
1
we will rock you
queen
121
1
the show must go on
queen
276
1
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论