1


the code below should convert the content of a file to uppercase (if conversion is necessary, it replaces the original character with the uppercase). The code is working on Mac OS and Linux. However on Windows, the conversion stops at the second letter and writes this second letter (uppercase) never ending in the file.

Example

Source data: asdf
Result: ASSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS...

I don't understand, why it's working on the other plattforms but for windows.

I'm very thankfull for any explanations.

regards, eljobso

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

int to_upper_file(FILE *fp)
{
    char ch = ' ';

    if (fp == NULL)
    {
        perror("Unable to open file");
        exit(0);
    }
    else
    {
        while (ch != EOF)
        {
            ch = fgetc(fp);
            if ((ch >= 'a') && (ch <= 'z'))
            {
                ch = ch - 32;
                fseek(fp, -1, SEEK_CUR);
                fputc(ch, fp);
            }
        }

    }
    fclose(fp);

    return 0;
}


int main(void)
{
    FILE *fp;
    int status;
    char filename[20];

    printf("Enter filename:");
    scanf("%s", filename);
    fp = fopen(filename, "r+");

    if(!fp)
    {
        printf("File error.\n");
        exit(0);
    }
    else
    {
        status = to_upper_file(fp);

        if (status == 0)
        {
            printf("Conversion success.\n");
        }
        if (status == -1)
        {
            printf("Conversion failure.\n");
        }
    }

    return 0;
}
alk
  • 69,737
  • 10
  • 105
  • 255
eljobso
  • 352
  • 2
  • 6
  • 20
  • See http://stackoverflow.com/questions/1713819/why-fseek-or-fflush-is-always-required-between-reading-and-writing-in-the-read-w (Code works "on Mac" and "on Linux" and "on Windows" means *nothing*. It's the libraries that your compilers use on each platform that count.) – Jongware May 25 '14 at 11:06
  • 1
    Everything @alk said, plus the ch variable needs to be an integer, not a character - fgetc needs to return all possible characters **plus** "ops, sorry, I couldn't read any character"; if EOF were a normal character, you'd stop the first time you see it, even if the file goes on after that. – loreb May 25 '14 at 12:31

1 Answers1

4

Due to the buffered nature of operations on a FILE * it is necessary when switching between reading and writing or writing and reading to perform a file-positioning operation or (for the latter) just a flushing.

From the MSDN documentation to fopen():

When the "r+", "w+", or "a+" access type is specified, both reading and writing are allowed (the file is said to be open for "update"). However, when you switch from reading to writing, the input operation must encounter an EOF marker. If there is no EOF, you must use an intervening call to a file positioning function. The file positioning functions are fsetpos, fseek, and rewind. When you switch from writing to reading, you must use an intervening call to either fflush or to a file positioning function.

To have your code fullfil this rule you might like to add a 0-seek after putting the character, like this:

    fseek(fp, -1, SEEK_CUR);
    fputc(ch, fp);
    fseek(fp, 0, SEEK_CUR);

Alternativly you could also flush the file:

    fseek(fp, -1, SEEK_CUR);
    fputc(ch, fp);
    fflush(fp);

Also it should be mentioned that error checking shall be be done for all relevant system calls.


Update:

From the C11 Standard (7.21.5.3/7 The fopen function) (Italics by me):

When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters endof- file. [...]

alk
  • 69,737
  • 10
  • 105
  • 255
  • Do you happen to know if this is *specific* to Microsoft's implementation of `fseek` *et cousines*? As the OP noticed, it "works" on other platforms (with my added note on library implementations), and so it's funny⇔noteworthy your quote indeed comes from MSDN. – Jongware May 25 '14 at 11:48
  • 2
    As the update to my question shows, its in the Standard. I 1stly quoted SMDN because the OP's problem referred to this platform, and as we all know it does not necessarly stick to any standards (and we read the MS specs **are** differnet from the C-Standard, as they add a rule). That the code *seems* to work on other platforms might due to the mystical nature of the famous *Undefined Behaviour*. @Jongware – alk May 25 '14 at 11:57
  • 2
    Excellent! So indeed, the OP needs not adjust his code because "it does not work" on Windows but rather *despite* it worked on other platforms. I'd upvote again for digging a bit deeper, if only SO rules allowed so. – Jongware May 25 '14 at 12:01
  • Referring the update to me comment above "*(and we read the MS specs are differnet from the C-Standard, as they add a rule)*": This is wrong, but I couldn't remove it anymore. – alk May 25 '14 at 12:04