Create a file if one doesn't exist - C
Asked Answered
T

2

48

I want my program to open a file if it exists, or else create the file. I'm trying the following code but I'm getting a debug assertion at freopen.c. Would I be better off using fclose and then fopen immediately afterward?

FILE *fptr;
    fptr = fopen("scores.dat", "rb+");
    if(fptr == NULL) //if file does not exist, create it
    {
        freopen("scores.dat", "wb", fptr);
    } 
Tantra answered 23/3, 2012 at 14:9 Comment(0)
B
67

You typically have to do this in a single syscall, or else you will get a race condition.

This will open for reading and writing, creating the file if necessary.

FILE *fp = fopen("scores.dat", "ab+");

If you want to read it and then write a new version from scratch, then do it as two steps.

FILE *fp = fopen("scores.dat", "rb");
if (fp) {
    read_scores(fp);
}

// Later...

// truncates the file
FILE *fp = fopen("scores.dat", "wb");
if (!fp)
    error();
write_scores(fp);
Billings answered 23/3, 2012 at 14:11 Comment(4)
It looks like the OP is trying to open it in read-only mode if it already exists (I don't understand why they want to create it at all in that case, though).Pagandom
Well, if he doesn't want to write to it, he doesn't need to. As far as I know, there's no way to open read-only on the condition that it exists without creating a needless race condition.Billings
@JamesMcLaughlin I thought the rb+ will let me write also? After this section of code I need to read everything from the file, then later write the file from scratch.Tantra
fseek doesn't work if you open file in "ab+" mode.Class
G
11

If fptr is NULL, then you don't have an open file. Therefore, you can't freopen it, you should just fopen it.

FILE *fptr;
fptr = fopen("scores.dat", "rb+");
if(fptr == NULL) //if file does not exist, create it
{
    fptr = fopen("scores.dat", "wb");
}

note: Since the behavior of your program varies depending on whether the file is opened in read or write modes, you most probably also need to keep a variable indicating which is the case.

A complete example

int main()
{
    FILE *fptr;
    char there_was_error = 0;
    char opened_in_read  = 1;
    fptr = fopen("scores.dat", "rb+");
    if(fptr == NULL) //if file does not exist, create it
    {
        opened_in_read = 0;
        fptr = fopen("scores.dat", "wb");
        if (fptr == NULL)
            there_was_error = 1;
    }
    if (there_was_error)
    {
        printf("Disc full or no permission\n");
        return EXIT_FAILURE;
    }
    if (opened_in_read)
        printf("The file is opened in read mode."
               " Let's read some cached data\n");
    else
        printf("The file is opened in write mode."
               " Let's do some processing and cache the results\n");
    return EXIT_SUCCESS;
}
Gabon answered 23/3, 2012 at 14:12 Comment(4)
This has a race condition, it will fail if another process creates the file between the two calls to fopen.Billings
@DietrichEpp Hadn't though of that!Gabon
If the initial point "If fptr is NULL, then you don't have an open file. Therefore, you can't reopen it." explains the behaviour the OP is seeing (which I believe it does), then, for clarity, it might be worth editing the text to seperate that point from the subsequent approach to fixing it. (also reopen should be freopen)Divalent
@Gabon - you're welcome. I think the other answer fails to identify the key point that it will never work. So folks might read that answer and their take away might be about race conditions, especially at it has been upvoted, when it is actually much much simpler. I could imagine spending hours trying to nail the race condition (which isn't there :-)Divalent

© 2022 - 2024 — McMap. All rights reserved.