简体   繁体   中英

How to write string to file in different lines in C

I have program that replaces a word in a file with another one, but in the new file the lines are all written as one line, not in different lines and paragraphs as required.

I tried adding '\\n' at the end of each line I am reading from the original file, but it is not working.

This is my code:

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;
}

The simple reason is that you are not outputting a newline to the file. The fscanf doesn't include the newline in s (because you specifically omit it with [^\\n] , which means "characters other than newline").

If you just add putc('\\n', f2); at the very end of the outer while loop, it works fine.

Alternatively, you could just read with fgets , which does include the newline in the string. An added benefit is that fgets forces you to specify the maximum length as an argument, while guarding against excessive line length with fscanf requires you to put the length in the format string itself.

(Note that the printf("%s", s); has no effect on what goes into the file, since it outputs to stdout .)

You should use fgets() to read from the input file instead of fscanf(f1,"%[^\\n]%*c", &s) for multiple reasons:

  • you do not give fscanf() to maximum number of characters to store into s : any sufficiently long line in the input file will cause undefined behavior.
  • you read the line from f1 and explicitly skip the newline, this explains why the newline never gets written to f2 .
  • fscanf() will fail on an empty line because there are no characters different from \\n to read into s , s is unmodified and gets handled like the previous line (or undefined behavior on the first line), and the loop iterates at the same spot in the input file, effectively stuck forever writing to f2 to no avail...

Here is a corrected and simplified version:

#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;
}

Note however that if the input file has very long lines with matches spanning multiple chunks read by fgets() , these matches will be missed by the program.

Here is a different approach to avoid this problem:

#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;
}

a first error is to give the address of s to fscanf, fscanf(f1,"%[^\\n]%*c",&s) must be fscanf(f1,"%[^\\n]%*c",s)

anyway, just replace your fscanf by a simple fgets and all will be ok, you will not loose \\n

PS if you cannot be sure MAX is not enough to handle lines you have to manage the case a line is cut in several at read, and may be the word to replace in cut because of that. There are several ways to do that.

So you want modify words in a file, but keep all whitespace as is? Then it is important to read and write the whitespace. Using read-functions that skips whitespace won't help you much.

Here is a generic version of a read-modify loop. Fixing bugs and extending it to a compete program, including error handling, is left as exercise to the reader

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"); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM