简体   繁体   中英

fopen and fprintf in C not working as intended?

I'm trying to write a program that adds line numbers to an already existing txt file.

For example, if the file is currently:

Hello
this is
an
exercise

Then after running the code, it will be:

(1) Hello
(2) this is
(3) an
(4) exercise

I wrote this code:

#include<stdio.h>
#include<conio.h>
FILE *fp;
void main()
{
    int counter=1;
    char newline;
    fp=fopen("G:\\name.txt","r+");
    if(fp==NULL)
        printf("Failed to open file!");
    fprintf(fp,"(%d)",counter);
    newline=fgetc(fp);
    while(newline!=EOF)
    {
        if(newline=='\n')
        {
            counter++;
            fprintf(fp,"(%d)",counter);
        }
        newline=fgetc(fp);
    }
    printf("All done!");
    getch();
    fclose(fp);
}

And the output is strange.

Firstly, it does not print at the beginning of the file. For some reason, it starts at the end of the file. And another strange thing that happens is that only the first print is successful.

The ones inside the while loop are gibberish (looks like small dots, doesn't resemble numbers at all)

When I use "r+" inside the fopen, the entire data is deleted, and all I can see is (1) and then gibberish.

If I use "a+" inside the fopen, it starts at the end of the file, and then writes (1) and gibberish.

AFAIK you basically cannot "insert" bytes in the middle of a file. Instead you will be overwriting bytes in the file. Thus when you use the same file for both reading and writing, you will be "interfering with yourself". I suggest you create a temporary file for the written output, or just write to standard output, allowing you to pipe the output to a suitable location.

Here is my suggestion:

#include <stdio.h>
// None conio.h
// Not global variable fp

#include <stdlib.h>   // To use exit()

void handle_error(char* message);  
int is_a10power(int n);

int main(void)   // Standard form of main()
{ 
    FILE *fp1 = fopen("G:\\name.txt","r");  // 1st file open for reading only
    if (fp1 == NULL)
        handle_error("Error open file for reading\n");
    FILE *fp2 = fopen("G:\\name2.txt","w");  // 2nd file open for writting only
    if (fp1 == NULL)
        handle_error("Error open file for writting\n");

    int io_result;

    char c = '\n';       // The name "newline" is not adequate: changed by "c"
    int counter = 0;    // Start in 0 because increment will be done at the beginning
    int no_digits = 0;  // Number of digits of "counter"

    for(;;) // Loop 
    {
        if (c == '\n')    // End-of-line handling
        {
          counter++;
          if (is_a10power(counter))
              no_digits++;

          io_result = fprintf(fp2,"(%d) ", counter);
          if (io_result < 3 + no_digits)  // Error: not all bytes could be written to fp2
          handle_error("Cannot write file\n");
        }

         c = fgetc(fp1);  // Reads only 1 character from fp1
         if (ferror(fp1))
               handle_error("Cannot read file\n"); 

         if (c == EOF)
               break;  // <---------  Loop exits here

         io_result = fprintf(fp2,"%c", c);
         if (io_result < 1)  // Less than 1 bytes transmited implies "fail"
              handle_error("Cannot write file\n");
    }

    io_result = fclose(fp1);
    if (io_result == EOF)
         handle_error("Close file error\n");
    io_result = fclose(fp2);
    if (io_result == EOF)
        handle_error("Close file error\n");

    printf("All done!");  // The success message goes after all operations are finished
    getchar();    // I like more getchar() than getch()
    return 0;    // Success
}

void handle_error(char* message)
{
    printf("%s",message);
    exit(EXIT_FAILURE);   // If error, end the program
}

int is_a10power(int n)
{
   while (n % 10 == 0)
       n /= 10;
   return (n == 1);
}

It seems I am obsessive with I/O errors.
So, another style could be the following:

#include <stdio.h>
#include <stdlib.h>   // To use exit()

FILE* safe_fopen(const char restrict * filename, const char restrict * mode);
void  safe_fclose(FILE* F);
void  safe_fprintf_int(FILE* F, int n);
void  safe_fprintf_char(FILE* F, char c);
int   safe_fgetc(FILE* F);

void handle_error(char* message);  

int main(void)
{ 
    FILE *fp1 = safe_fopen("G:\\name.txt","r");  // 1st file open for reading only
    FILE *fp2 = safe_fopen("G:\\name2.txt","w");  // 2nd file open for writting only

    char c = '\n';
    int counter = 0;
    for(;;) // Loop 
    {
        if (c == '\n')
           safe_fprintf_int(fp2, ++counter);    // End-of-line handling
        c = safe_fgetc(fp1);
        if (c == EOF)
           break;  // <---------  Loop exits here
        safe_fprintf_char(fp2, c);
    }
    safe_fclose(fp1);
    safe_fclose(fp2);        
    printf("All done!");
    return 0; 
}

void handle_error(char* message)
{
    printf("%s",message);
    exit(EXIT_FAILURE);   // If error, end the program
}


FILE* safe_fopen(const char restrict * filename, const char restrict * mode)
{
   FILE* F = fopen(filename, mode);
   if (F == NULL)
      handle_error("Cannot open file");
   else
      return F;
}
void  safe_fclose(FILE* F)
{
   if (fclose(F) == EOF)
      handle_error("Error closing file");
}

int no_digits(int n)
{
    int nod = 0;
    for ( ; n != 0; n /= 10)
        nod++;
    return nod;
}

void  safe_fprintf_int(FILE* F, int n)
{
    if (fprintf(F,"(%d) ",n) < 3 + no_digits(n))
      handle_error("Error writting digits to file");
}

void  safe_fprintf_char(FILE* F, char c)
{
    if (fprintf(F,"%c",c) < 1)
        handle_error("Cannot write character in file");
}

int   safe_fgetc(FILE* F)
{
    int r = fgetc(F);
    if (ferror(F))
         handle_error("Cannot read file");
    else
         return r;
}

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