4

I have this code:

while( (cCurrent = fgetc(fp)) != EOF)
{

}

The problem is, when it hits a new line it stops reading.

What would be a good way to ignore the newline character?

Edit:

I'm trying to create a file crypter. It is able to encrypt the file, but the decrypt process won't work. It works till the end of the first line, but it won't continue to the next characters in the file.

For example, for the text file:

Foo  
Bar

After the encryption the result is:

Xu||Gb|t

After the decryption the result is:

FooRqb

I concluded that the new line char was the problem. maybe it wasn't.

My code is:

/* check if the file is openable */
if( (fp = fopen(szFileName, "r+")) != NULL )
{
    /* save current position */
    currentPos = ftell(fp);
    /* get the current byte and check if it is EOF, if not then loop */
    while( (cCurrent = fgetc(fp)) != EOF)
    {
        /* XOR it */
        cCurrent ^= 0x10;
        /* take the position indicator back to the last position before read byte */
        fseek(fp, currentPos, SEEK_SET);
        /* set the current byte */
        fputc(cCurrent, fp);
        /* reset stream for next read operation */
        fseek(fp, 0L, SEEK_CUR);
        /* save current position */
        currentPos = ftell(fp);
    }
Lior
  • 5,841
  • 9
  • 32
  • 46

4 Answers4

7

It took me a while, but I finally got what you're trying to do.

Input file:

Hello

encrypt by running your code:

Xu||(non displayable character)

Decrypt by running your code again:

Hello

So how this works:

0x48 XOR 0x10 = 0100 1000 (H)---+
                0001 0000       |
                ---------       V
                0101 1000 = 58 (X)

0x58 XOR 0x10 = 0101 1000 (X)---+
                0001 0000       |
                ---------       V
                0100 1000 = 48 (H)

The problem comes in with you're working with the new line character '\n' which is 0xA16

Input file:

Hello
You

This works fine up until the '\n' then we get new line:

0xA XOR 0x10 =  0000 1010 ('\n')---+
                0001 0000          |
                ---------          V
                0001 1010 = 1A (substitute character)

The substitute character In DOS operating systems, this character is used to indicate the end of a file (EOF)

So this fails because you're working on Windows. So you need to do special checking for the '\n' case in your encryption/decryption and not blindly XOR it.

One easy fix, you could simply do something like:

while( (cCurrent = fgetc(fp)) != EOF)
{
    /* XOR it if it's not a '\n'*/
    if(cCurrent != '\n')
      cCurrent ^= 0x10;
Mike
  • 47,263
  • 29
  • 113
  • 177
  • impressive, I saw the 0x1a with hexdump, but didn't give it much thought as I've read in the wiki that EOF is not used by the OS to determine the file size, and that it was used on DOS only. – iabdalkader Nov 07 '12 at 16:58
  • 1
    @user1718294 - Happy to help. It was a good question that took some thinking about. I'd up vote you but I ran out of up votes for the day. :) – Mike Nov 07 '12 at 17:35
4

On Windows, lines in text files are separated by \r\n, not just \n, and files are opened by default in "text" mode, which automatically translates \r\n to \n when reading the file (see fopen in Visual Studio 2012).

Since you're interpreting your file as a sequence of bytes (because of the XOR operation), you don't want this behavior - each time there is a line ending, you're losing a byte of data. You should open your file in "binary" mode to suppress this behavior:

fp = fopen(szFileName, "rb+")

This will also suppress the behavior noted by @Mike where reading a \x1A character is interpreted as the end of file.

prprcupofcoffee
  • 2,950
  • 16
  • 20
2

fgetc shouldn't stop at a newline character, only at EOF, from man fgetc(3):

fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.

If you however, write it like this:

while( (cCurrent = fgetc(fp)) != '\n' && cCurrent != EOF)

It will stop at the newline character, so the first one is correct:

while( (cCurrent = fgetc(fp)) != EOF)
iabdalkader
  • 17,009
  • 4
  • 47
  • 74
  • You are right, my solution wasn't good. But still, it stops when it hits a new line. – Lior Nov 07 '12 at 15:51
  • @user1718294 how to you open the file ? do you open for reading and writing ? also your logic seems wrong, you keep resetting the file pointer so you read the characters you just wrote. – iabdalkader Nov 07 '12 at 16:09
  • I'm using `fopen` with 'r+'. I'm not reading the same characters that I wrote. I'm writing to the same characters that I read. – Lior Nov 07 '12 at 16:11
  • @user1718294 that's correct, I've tried your code seems to be working well, I seem no problems, except that fseek(fp, 0L, SEEK_CUR) is useless you don't need it, try with another text file maybe. – iabdalkader Nov 07 '12 at 16:17
  • When I'm removing `fseek(fp, 0L, SEEK_CUR);` it gets me into an infinite loop. – Lior Nov 07 '12 at 16:20
  • @user1718294 it shouldn't, that line does nothing, it seeks zero bytes from the current position. – iabdalkader Nov 07 '12 at 16:21
  • Note that ANSI C requires that a file positioning function intervene between output and input, unless an input operation encounters end-of-file. (If this condition is not met, then a read is allowed to return the result of writes other than the most recent.) Therefore it is good practice (and indeed sometimes necessary under Linux) to put an fseek(3) or fgetpos(3) operation between write and read operations on such a stream. This operation may be an apparent no-op (as in fseek(..., 0L, SEEK_CUR) called for its synchronizing side effect. – Lior Nov 07 '12 at 16:25
  • @user1718294 very good point ! unfortunately it's still the same, your code is working here fine, on Linux, encrypts and decrypts, with or without the seek. – iabdalkader Nov 07 '12 at 16:31
  • @mux - see my answer as to why it's working on Linux, not on Windows – Mike Nov 07 '12 at 16:39
0

This is not the correct behavior for fgetc().

Your second code snippet is puzzling as it does in fact break when you hit a newline. So how that resolves the issue is a mystery to me.

I suspect you have your logic mixed up a little.

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • You are right, my solution wasn't good. But still, it stops when it hits a new line. – Lior Nov 07 '12 at 15:52