0

I'm reading and writing (append mode) into a file, and something very strange happens. The first time I run it, it reads the file perfectly, and appends the new line perfectly. The second time, it gets stuck reading the file.

What am I doing wrong?

The reading:

fptr = fopen("log.txt", "r");
fseek(fptr, -70, SEEK_END); 
for(i = 0; i < 11; i++)
{
    fscanf(fptr, "%d, ", llast + i);
    printf("%d ", llast[i]);
}

/* read the last review */    
for(i = 0; i < 11; i++)
{
    fscanf(fptr, "%d, ", last + i);
    printf("%d ", last[i]);
}

fclose(fptr);

The appending:

fptr = fopen("log.txt", "a");
for(i = 0; i < 11; i++)
{
    fprintf(fptr, "%d, ", *(new + i));
}
fprintf(fptr, "\n");
fclose(fptr); 

The log.txt file last two lines contain:

7, 4, 0, 10, 2, 8, 9, 5, 6, 3, 1, 
3, 7, 5, 6, 9, 2, 4, 10, 0, 1, 8, 

The Full Code:

#include <stdio.h>
#include <stdlib.h> /* for the rand function */

int main()
{
int last[11] = {0};
int llast[11] = {0};
int new1[11] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
int i = 0, j = 0;
int rev = 0;
int flag = 0;
int big_flag = 0;
FILE *fptr;

printf("\nThe last review:\n");
PrintRev();
printf("\n\n");

/* open the log file of the reviews, and load the last two into arrays */
fptr = fopen("log.txt", "r");

/* read the review before the last review*/
fseek(fptr, -70, SEEK_END); 

for(i = 0; i < 11; i++)
{
    fscanf(fptr, "%d, ", llast + i);
    printf("%d ", llast[i]);
}

/* read the last review */    
for(i = 0; i < 11; i++)
{
    fscanf(fptr, "%d, ", last + i);
    printf("%d ", last[i]);
}

fclose(fptr);

/* create a new arrangement of reviewing */

/* the big flag is used for the extreme case when the first 10 reviewers are assigned
   but a problem occurs in the last remaining option for the last reviewer -
   so instead of running into a dead end of endless-loop, if there is a problem,
   we start over again ... */
while(!big_flag)
{
    for(i = 0; i < 10; i++)
    {
        flag = 0;
        /* the small flag is used to make sure the first 10 reviewers follow the rules */
        while(!flag)
        {
            rev = (rand() % 11);
            if((rev != i) && /* you can't review yourself */
               (rev != last[i]) &&  /* you can't review the person you did last time */
               (rev != llast[i]) && /* let's try without the last-last time */
               (rev != (i - 1)) && (rev != (i + 1)) && /* you can't review your neighbour */
               (new1[rev] != i)) /* you can't review the person reviewing you */
            {
                flag = 1;                
                /* a reviewer can't review more than one person... so we check if he wasn't
                   chosen before */
                for(j = 0; j < i; j++)
                {
                    if(rev == new1[j])
                    {
                        flag = 0;
                    }
                }
            }
        }
        new1[i] = rev;
    }

    /* find the only remaining id of reviewer */
    rev = 55; /* sum of 0,1, ... 10 */
    for(i = 0; i < 10; i++)
    {
        rev -= new1[i];
    }
    /* check if the remaining id is valid */ 
    if((rev != i) && 
       (rev != last[i]) &&  
       (rev != llast[i]) && /* let's try without the last-last time */
       (rev != (i - 1)) && 
       (new1[rev] != i))
    /* if so, appoint it, and raise the big flag to exit the loop */
    {
        new1[10] = rev;
        big_flag = 1;
    } 
}

/* append the new arrangement to file */
fptr = fopen("log.txt", "a");
for(i = 0; i < 11; i++)
{
    fprintf(fptr, "%d, ", *(new1 + i));
}

fclose(fptr); 




return 0;
}
Maverick Meerkat
  • 5,737
  • 3
  • 47
  • 66
  • Could you please explain me what `new` mean in `C`? – xenteros Aug 17 '16 at 08:20
  • @xenteros it's just the name of my new array holding the new values to be appended. Maybe it's a poor choice of name, since I think new has some meaning in C++, but it doesn't affect the code. I changed it to new1, and it still acts exactly the same... – Maverick Meerkat Aug 17 '16 at 08:23
  • Generally speaking, `fseek()` is not dependable on text files. – jxh Aug 17 '16 at 08:30
  • @jxh - any other suggestion of what to use? I need to always read the last two lines of the log file... – Maverick Meerkat Aug 17 '16 at 08:31
  • It's a poor choice of name that's right since 1) it does not describe the variable and 2) it's a common protected word in many languages. Anyway if in the code you show you refer to a variable or function, please be sure that its declaration IS in the code you show. – Tim Aug 17 '16 at 08:32
  • I don't see why `-70` will always get you to the beginning of the last two lines. Myself, I would probably `mmap` the file, and search for the last two lines. – jxh Aug 17 '16 at 08:36
  • @jxh - each line contains exactly 34 chars + the EOL character. I think. But you might be right. How can I write it using mmap? – Maverick Meerkat Aug 17 '16 at 08:40
  • 3
    For streams open in text mode, offset shall either be zero or a value returned by a previous call to ftell, and origin shall necessarily be SEEK_SET. – n. m. could be an AI Aug 17 '16 at 08:43
  • 1
    `mmap` example: http://stackoverflow.com/questions/20460670/reading-a-file-to-string-with-mmap – jxh Aug 17 '16 at 08:49
  • `fseek(fptr, -70, SEEK_END);` --> Check its result. – chux - Reinstate Monica Aug 17 '16 at 12:14
  • "something very strange happens" --> check return values of ` fscanf(fptr, ...`. Are they as expected? – chux - Reinstate Monica Aug 17 '16 at 12:17
  • It is not clear `rev`, as used as an index like `new1[rev]`, is _always_ in the value range 0 to 10. – chux - Reinstate Monica Aug 17 '16 at 12:21

1 Answers1

0

There is at least one problem in your code. You say that log.txt contains lines and that you add a new line at the end, but your code never output a single '\n', so you never write the EOL marker. As you say you use Linux, the end of line on disk is indeed a single character '\n', but your code would not work on Windows where it is the pair "\r\n" => said differently the present code is not portable.

But if you only want to run in on a Unix system, just add a new line after writing the 11 values:

/* append the new arrangement to file */
fptr = fopen("log.txt", "a");
for(i = 0; i < 11; i++)
{
    fprintf(fptr, "%d, ", *(new1 + i));
}
fputc('\n', fptr);
fclose(fptr); 

I could run it 5 times and it did add 5 lines (of exactly 35 characters...)

But beware: Here you are essentially processing a text file as if it was a binary file with records of size 35. That is not portable because end of lines are different across architecture, but it is dangerous for another reason: as it is a text file, it can be opened with a text editor, say vi. What would you expect if someone opens the file and inadvertently add some spaces at the end of one of the 2 last lines? The records will not be exactly 35 characters long and you will read wrong values. And as you fail to test any result of you input functions (neither open, nor fscanf) and you do not test that you have read values from 0 to 10 either, your program is likely to crash without any useful message.

TL/DR: do add tests for all your open and fscanf calls, and do control that every line contains all values from 0 to 10

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252