在 c 中读取文件会在开头添加奇怪的字符

Reading from file in c adds weird characters in the beginning

提问人:Catalin Ionita 提问时间:11/11/2023 最后编辑:chqrlieCatalin Ionita 更新时间:11/11/2023 访问量:82

问:

我必须从一个文件中读取 2 个字母,然后从同一文件的下一行读取文本(这 2 个字母用空格分隔)。现在,我需要在整个文本中用第二个字母替换第一个字母。

这是我的输入:

a x
abecedar avion nuca

所以这封信将被替换为 .ax

这是我的代码:

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

int main()
{
    FILE *in;
    in = fopen("in.txt", "r+");
    char litera1 = 0, litera2 = 0, s[100], aux[100] = "";

    fscanf(in, " %c", &litera1);
    fgetc(in);
    fscanf(in, " %c", &litera2);
    strcpy(aux, "");
    printf("%c %c\n", litera1, litera2);

    while (fscanf(in, "%s", aux) != EOF)
    {
        strcat(s, aux);
        strcat(s, " ");
    }
    s[strlen(s) - 1] = NULL;
    printf("%s\n", s);
    for (int i = 0; i < 9; i++)
        printf("%c\n", s[i]);

    for (int i = 0; i < strlen(s); i++)
    {
        if (s[i] == litera1)
            s[i] = litera2;
    }

    /*
    rewind(in);
    fprintf(in,"%s", s);    i have to overwrite in the same file
    fflush(in);
    fclose(in); */
    return 0;
}

这是我的输出:

a x
@§@abecedar avion nuca
@
§
@
a
b
e
c
e
d

在另一个程序中,我需要将大写字母更改为小写字母,反之亦然,交换工作正常,但我在文本前面得到了这个:

如何摆脱前三个字符?我一个字母一个字母地展示它以检查问题所在,但我无法弄清楚。我怀疑这是因为搬到了新生产线,但我不知道如何解决它。提前致谢。

C 文件

评论

3赞 Barmar 11/11/2023
与问题无关:应该是 。 用于指针。s[strlen(s)-1]=NULL;s[strlen(s)-1]='\0';NULL
5赞 Weather Vane 11/11/2023
这是一个问题。尚未初始化。你暗示了,但那是错误的字符串。strcat(s,aux);saux
1赞 Weather Vane 11/11/2023
旁白。你明白空间在什么地方做什么吗?你仍然用fscanf(in," %c", &litera2);fgetc(in);
0赞 Catalin Ionita 11/11/2023
总是那么简单,伙计......非常感谢!
3赞 Weather Vane 11/11/2023
到底应该做什么?它将字符串缩短 1,如果您使用 ,这可能是相关的,它保留了换行符,但 with 不保留。s[strlen(s)-1]=NULL;fgets()fscanf%s

答:

0赞 Allan Wind 11/11/2023 #1
  1. 您需要初始化,否则将是未定义的行为。要么使用然后以不同的方式处理返回值,要么像下面这样需要输入。sstrcat(s, ...)char s[LEN+1] = ""fscanf()
  2. 如果 -loop 未运行,将导致缓冲区下溢。whiles[strlen(s)-1] = ...
  3. 始终检查 的返回值,否则对文件句柄的后续函数调用可能会失败。fopen()
  4. 始终检查 的返回值,否则可能会对未初始化的数据进行操作。scanf()
  5. 读取带有 的字符串时,始终使用最大字段宽度。scanf()
  6. 始终检查生成的数组中是否有足够的空间。如果输入的第二行小于 9 个字节,则 print 语句将打印未初始化的数据。
  7. 完成后,这是一个很好的做法。fclose()
  8. 考虑使用 or 来获取一行而不是单词。如果连续或尾随空格是一个问题,您可以随后修补该行。fgets()getline()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LEN 100
#define str(s) str2(s)
#define str2(s) #s

int main(void) {
    FILE *in = fopen("in.txt", "r+");
    if(!in) goto err;

    char litera1;
    char litera2;
    if(fscanf(in, " %c %c", &litera1, &litera2) != 2) goto err;
    printf("%c %c\n", litera1, litera2);

    char s[LEN+1];
    char aux[LEN+1];
    if(fscanf(in, "%" str(LEN) "s", s) != 1) goto err;
    while(fscanf(in, "%" str(LEN) "s", aux) == 1) {
        if(LEN - strlen(s) < strlen(aux) + 1) {
            fprintf(stderr, "full: s=\"%s\", aux=\"%s\"\n", s, aux);
            break;
        }
        strcat(s, " ");
        strcat(s, aux);
    }
    printf("%s\n", s);
    size_t n = 9 < LEN ? 9 : LEN;
    for(size_t i=0; i<n && s[i]; i++)
        printf("%c\n", s[i]);

    fclose(in);
    return 0;
err:
    perror("");
    fclose(in);
    return 1;
}

输出为:

a x
abecedar avion nuca
a
b
e
c
e
d
a
r
 
0赞 chqrlie 11/11/2023 #2

问题来自未初始化,因此它可能在循环开始时不包含空字符串,并且此随机内容出现在输出中。sfor

您可以通过一次读取一个字节的文件内容并在需要时替换字母来大幅简化代码。

您在注释中指定必须覆盖同一文件:以这种方式更改文件内容似乎是您的任务,但通常最好使用修改后的内容生成一个新文件,这也更简单且更不容易出错。

若要就地修改文件,可以使用查找回写入修改内容的位置,也可以从写入切换回读取。但请注意,在旧平台上查找文本文件存在限制:要移动当前位置,您只能查找之前由 返回的位置。fseek()ftell()

这是修改后的版本:

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

int main() {
    FILE *in = fopen("in.txt", "r+");
    if (in == NULL) {
        fprintf(stderr, "cannot open %s: %s\n", "in.txt", strerror(errno));
        return 1;
    }
    int literal1 = fgetc(in);
    int space = fgetc(in);
    int literal2 = fgetc(in);
    int newline = fgetc(in);
    if (literal1 == EOF || space != ' ' || literal2 == EOF || newline != '\n') {
        fprintf(stderr, "invalid format: need 2 characters separated by a space\n");
        fclose(in);
        return 1;
    }
    for (;;) {
        long pos = ftell(in);   
        int c = fgetc(in);
        if (c == EOF)
            break;
        if (c == literal1) {
            // need to replace the letter:
            // seek back to the previous position
            fseek(in, pos, SEEK_SET);
            // overwrite the character with the replacement
            fputc(literal2, in);
            // seek to the current position to allow switching back
            //   to reading from `in`
            fseek(in, 0L, SEEK_CUR);
        }
    }
    fclose(in);
    return 0;
}