【问题标题】:How to write string to file in different lines in C如何在C中的不同行中将字符串写入文件
【发布时间】:2018-12-27 12:04:35
【问题描述】:

我有一个程序可以将文件中的一个单词替换为另一个单词,但在新文件中,所有行都写成一行,而不是按要求写成不同的行和段落。

我尝试在从原始文件读取的每一行末尾添加'\n',但它不起作用。

这是我的代码:

int main() {
    FILE *f1, *f2;
    char word[MAX], fname[MAX];
    char s[MAX], replace[MAX];
    char temp[] = "temp.txt", *p1, *p2;
    printf("Enter your input file name:");
    fgets(fname, MAX, stdin);
    fname[strlen(fname) - 1] = '\0';

    scanf("%s", word);

    scanf("%s", replace);

    f1 = fopen(fname, "r");
    if (!f1) {
        printf("Unable to open the input file!!\n");
        return 0;
    }
    f2 = fopen(temp, "w");
    if (!f2) {
        printf("Unable to open temporary file!!\n");
        return 0;
    }

    while (fscanf(f1,"%[^\n]%*c", &s) != EOF) {
        printf("%s",s); //I wanted to see what happens when I'm reading from the file. Previously I added at the end of string s the char '\n' but it didnt work

        if (strstr(s, word)) {
            p2 = s;
            while (p1 = strstr(p2, word)) {
                while (p2 != p1) {
                    fputc(*p2, f2);
                    p2++;
                }
                p1 = p1 + strlen(word);
                fprintf(f2, "%s", replace);
                p2 = p1;
            }
            while (*p2 != '\0') {
                fputc(*p2, f2);
                p2++;
            }
        } else {
            fputs(s, f2);
        }
    }

    fclose(f1);
    fclose(f2);

    remove(fname);

    rename(temp, fname);
    return 0;
}

【问题讨论】:

  • 所以您编写了一些代码,其中包含读取换行符以外的任何内容的指令,问题在于它不读取换行符?我不确定...

标签: c string file replace word


【解决方案1】:

简单的原因是您没有向文件输出换行符。 fscanf 不包括s 中的换行符(因为您专门用[^\n] 省略了它,这意味着“字符 以外的换行符”)。

如果您只是在外部while 循环的最后添加putc('\n', f2);,它就可以正常工作。

或者,您可以使用fgets 阅读,它确实在字符串中包含换行符。另一个好处是fgets 强制您将最大长度指定为参数,而使用fscanf 防止过长的行长度要求您将长度放入格式字符串本身。

(注意printf("%s", s); 对文件中的内容没有影响,因为它输出到stdout。)

【讨论】:

  • 有效!非常感谢。但我有个问题。为什么当我尝试在每个包含原始文件中每一行的字符串 s 的末尾添加 '\n' 时它不起作用?
  • @MaryPoppins 很难说没有看到您是如何尝试添加它的,但是当在阅读后添加如下:int len = strlen(s); s[len] = '\n'; s[len + 1] = '\0'; 时,它应该可以工作。 (但是,不需要这样做,只需使用fgets 阅读,它将被包含在内。或者,如果您真的想使用fscanf 阅读,请在末尾打印换行符 - 将其添加到字符串 正确 需要获取长度并检查边界。)
  • 警告正如我在回答中所说,如果通过 fgets 读取的行长于 MAX,您可能会切断该行并且两个 fgets 之间的单词不会被替换
【解决方案2】:

出于多种原因,您应该使用 fgets() 而不是 fscanf(f1,"%[^\n]%*c", &s) 从输入文件中读取:

  • 您没有将fscanf() 指定为要存储到s 中的最大字符数:输入文件中任何足够长的行都会导致未定义的行为。
  • 您从f1 读取了这一行并明确跳过了换行符,这就解释了为什么换行符永远不会被写入f2
  • fscanf() 将在空行上失败,因为没有与 \n 不同的字符可以读入 ss 未修改并像上一行一样处理(或第一行上的未定义行为),并且循环在输入文件中的同一位置进行迭代,有效地永远卡住写入f2 无济于事......

这是一个更正和简化的版本:

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

#define MAX  100

int main() {
    FILE *f1, *f2;
    char word[MAX], fname[MAX];
    char s[MAX], replace[MAX];
    char temp[] = "temp.txt";
    char *p1, *p2;

    printf("Enter your input file name: ");
    if (!fgets(fname, sizeof fname, stdin))
        return 1;
    fname[strcspn(fname, "\n")] = '\0';  /* strip the newline if present */

    printf("Enter the word to search: ");
    if (scanf("%99s", word) != 1)
        return 1;

    printf("Enter the replacement word: ");
    if (scanf("%99s", replace) != 1)
        return 1;

    f1 = fopen(fname, "r");
    if (!f1) {
        fprintf(stderr, "Unable to open the input file %s\n", fname);
        return 1;
    }
    f2 = fopen(temp, "w");
    if (!f2) {
        fprintf(stderr, "Unable to open temporary file %s\n", temp);
        return 1;
    }

    while (fgets(s, sizeof s, f1)) {
        p1 = s;
        while ((p2 = strstr(p1, word)) != NULL) {
            while (p1 < p2) {
                fputc(*p1++, f2);
            }
            fputs(replace, f2);
            p1 += strlen(word);
        }
        fputs(p1, f2);
    }

    fclose(f1);
    fclose(f2);

    remove(fname);
    rename(temp, fname);
    return 0;
}

但是请注意,如果输入文件有很长的行,并且匹配跨越多个由fgets() 读取的块,则程序将错过这些匹配。

这是避免此问题的另一种方法:

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

#define MAX  100

int main() {
    FILE *f1, *f2;
    char fname[MAX], word[MAX], replace[MAX];
    char temp[] = "temp.txt";
    char *p1 *p2;
    int c;

    printf("Enter your input file name: ");
    if (!fgets(fname, sizeof fname, stdin))
        return 1;
    fname[strcspn(fname, "\n")] = '\0';  /* strip the newline if present */

    printf("Enter the word to search: ");
    if (scanf("%99s", word) != 1)
        return 1;

    printf("Enter the replacement word: ");
    if (scanf("%99s", replace) != 1)
        return 1;

    f1 = fopen(fname, "r");
    if (!f1) {
        fprintf(stderr, "Unable to open the input file %s\n", fname);
        return 1;
    }
    f2 = fopen(temp, "w");
    if (!f2) {
        fprintf(stderr, "Unable to open temporary file %s\n", temp);
        return 1;
    }

    p2 = word;
    while ((c = getc(f1)) != EOF) {
        if (c != '\0' && *p2 == (char)c) {
            p2++;
            if (*p2 == '\0') {
                fputs(replace, f2);
                p2 = word;
            }
        } else {
            for (p1 = word; p1 < p2;) {
                putc(*p1++, f2);
                /* find potential match for special cases: find aab in aaab */
                if (!memcmp(word, p1, p2 - p1) && word[p2 - p1] == (char)c)
                    p2 = word + (p2 - p1) + 1;
                    p1 = word;
                    break;
                }
            }
            if (p1 == p2) {
                putc(c, f2);
            }
        }
    }
    /* flush potential partial match at end of file */
    for (p1 = word; p1 < p2; p1++) {
        putc(*p1, f2);
    }
    fclose(f1);
    fclose(f2);

    remove(fname);
    rename(temp, fname);
    return 0;
}

【讨论】:

    【解决方案3】:

    第一个错误是将s的地址给fscanf,fscanf(f1,"%[^\n]%*c",&amp;s)必须是fscanf(f1,"%[^\n]%*c",s)

    无论如何,只需将您的 fscanf 替换为一个简单的 fgets 就可以了,您不会丢失 \n

    附:如果您不能确定 MAX 不足以处理行,那么您必须处理在读取时一行被剪切成几行的情况,并且可能因此在 cut 中替换掉这个词。有几种方法可以做到这一点。

    【讨论】:

    • 我尝试用不带地址的 fscanf 替换它,虽然我不明白为什么,它仍然无法正常工作。我也试过 fgets 版本,但更糟糕的是,现在,用 fgets 读取一个字符串中的所有文件并替换单词,最终文件不完整。
    • 要删除 & 只允许给 fscanf 一个 char * 而不是 char **,因为这是预期的类型。
    • @bruno &amp;s 不是这里问题的原因,地址还是一样的,所以只要char *char (*)[MAX] 具有相同的表示形式,两者都适用。 (但是,是的,没有&amp;s 是正确的。)
    • 我说使用 fgets 是因为您需要逐行阅读才能将单词替换为另一个单词,您不需要为此使用正则表达式
    • @Arkku 抱歉,但对我来说,你的言论是灾难性的,即使我们 only 在 c 而不是 c++ 中,也必须尊重预期的类型
    【解决方案4】:

    所以你想修改文件中的单词,但保留所有空格?然后读取和写入空格很重要。使用跳过空格的读取函数对您没有多大帮助。

    这是一个通用版本的读取-修改循环。修复错误并将其扩展到完整的程序,包括错误处理,留给读者作为练习

    while (1)
      {
        // Read the next character
        int ch = fgetc(infile);
        if (ch == EOF)
           break;  // read error or eof
    
        if (isspace(ch))
           {
              // The character was a whitespace, so we copy it to the outputstream
              int err = fputc(ch, outfile);
              if (err == EOF)
                break; // error
           }
        else
          {
            // The next character was not a whitespace, so we put it back in the
            //  inputstream for scanf to find it
            ungetc(ch, instream);
    
            char word[64]; // Just assume for simplicity that no words are longer 
                           // than 63 character.
    
            // Read the next string, making sure we don't read more than the buffer
            // can handle. A robust program should do something useful if words are
            // actually longer than 63 characters
            int len = fscanf(infile, "%63s", word);
    
            if (len == EOF)
               break;  // error (should not happen since there is guarantied to be
                         // one non-whitespace character in the stream)
    
            char mod_word[64];
            modify_word(mod_word, word);
    
            int err = fputs(mod_word, outfile);
            if (err == EOF)
               break; // error
         }
    }
    
    // Check for read-write errors
    if (ferror(infile))
      perror("Failure reading from input file");
    if (ferror(outfile))
      perror("Failure writing to output file"); 
    

    【讨论】:

    • 请注意,目标词(被替换)的长度是已知的,所以如果最大词长是一个问题,只缓冲到目标词的长度就足够了并将任何溢出直接复制到输出(因为更长的单词无法匹配)。
    • @Arkku,是的,也可以在从输入流中读取单个字符时匹配单个字符,而无需缓冲整个单词。但我只是希望它通用且简单。无需一次教初学者许多技巧:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    • 2019-09-23
    相关资源
    最近更新 更多