2

With printArea() I want to print an Array into a file, with scanArea() I want to exactly scan this file and paste it into my array.

So logically if I do printArea() & scanArea() I should get the same result as before? Well, no. I got some line breaks. Any ideas to my code?

int m = 0;
int n = 0;
int c = 0;

void printArea (int len, char p[][len])
{
FILE *out = fopen ("spielfeld.txt", "w");
for (int i = 0; i < len; i++)
{
    for (int j = 0; j < len; j++)
        fputc(p[i][j], out);
    fputc(10, out);
}
fclose(out);
}

int scanArea   (int len, char p[][len])
{
FILE *in = fopen ("spielfeld.txt", "r");

if (in == 0)
    return 1;
else
{
    while ((c=fgetc(in)) != EOF)
    {
        if ((c=fgetc(in)) == 10)
        {
            c++;
            m++;
            n = 0;
        }
            p[m][n] = (c=fgetc(in));
            n++;
    }
    return 0;
}
fclose(in);
}

called by:

int len;
len = 12;
char arr[len][len];

showArea (len, arr);
printArea(len, arr);
scanArea (len, arr);
showArea (len, arr);

first and second showArea differ.

111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111

111111111111
1111
1111111
1111
1111111
1111
1111111
1111
1111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
s55michi
  • 67
  • 1
  • 6
  • 6
    Don't hard-code character codes, use `'\n'` instead of `10`. – Barmar Dec 14 '14 at 23:32
  • `printArea` assumes that every line is the same length. `scanArea` starts a new row in the array whenever it sees a newline. – Barmar Dec 14 '14 at 23:33
  • Could you show the declaration of the array in the function that calls these two functions? What is in the original array and the resulting array? – Barmar Dec 14 '14 at 23:36
  • fprintf(out, "\n") gives the same result – s55michi Dec 14 '14 at 23:38
  • Is this on Windows? If so, you are getting a free `\r` with every `\n`, compliments of mr. Bill Gates. Check your output file with a hex editor. – Jongware Dec 14 '14 at 23:51
  • yes, it's windows. checking with hxd right now – s55michi Dec 15 '14 at 00:02
  • 1
    @Jongware: note that the files are opened as text files (no `b` in the mode), so the I/O system should be mapping the CRLF line endings to just NL on input, and just NL to CRLF on output. If the file was opened in binary mode, that mapping would not happen. – Jonathan Leffler Dec 15 '14 at 00:35
  • your reading upto three times on every itteration of the input loop, This will cause characters to be lost. Rather only read once, and thereafter, within the loop, use the value in the variable 'c'. – user3629249 Dec 15 '14 at 03:20

1 Answers1

0

Your main loop in scanArea() has problems. It is currently:

while ((c=fgetc(in)) != EOF)
{
    if ((c=fgetc(in)) == 10)
    {
        c++;
        m++;
        n = 0;
    }
    p[m][n] = (c=fgetc(in));
    n++;
}

The loop condition reads a character. The if statement immediately reads the next character, jettisoning the first. You then read yet another character to assign to the array, jettisoning the second too. This means you skip two-thirds of the characters. You also increment c when it's a newline, which is odd and pointless. Newline should be written '\n' and not as 10.

You need something that's less enthusiastic about reading characters:

while ((c = fgetc(in)) != EOF)
{
    if (c == '\n')
    {
        m++;
        n = 0;
    }
    else
        p[m][n++] = c;
}

This is still a bit dangerous; it will store any number of any sort of character except newline into the array, without concerns about the bounds on the array, either on a single line or number of lines in total. But it doesn't skip two-thirds of the data.

On further review, I observe that m, n and c are shown as global variables; they should be local to scanArea(). You should avoid global variables whenever possible. When necessary, they should be made static in a file unless they really must be shared between source files. Then you need to follow the guidelines in How do I use extern to share variables between source files to make sure everything is consistent.

There is only one scanArea() and m, n and c are only used there.

Then there is no excuse for them not being local. Indeed, if you called scanArea() twice, the fact that these variables are global would mean that on the second invocation, the indexing would start at an array location that is way out of bounds and get worse from there onwards.


Working code

I make functions static until there's a proven need for them to be visible in another source file. Not everyone is as anal retentive about this as I am.

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

static void printArea(int len, char p[len][len])
{
    FILE *out = fopen("spielfeld.txt", "w");
    for (int i = 0; i < len; i++)
    {
        for (int j = 0; j < len; j++)
            putc(p[i][j], out);
        putc('\n', out);
    }
    fclose(out);
}

static int scanArea(int len, char p[len][len])
{
    FILE *in = fopen("spielfeld.txt", "r");

    if (in == 0)
        return 1;
    else
    {
        int m = 0;
        int n = 0;
        int c;
        while ((c = getc(in)) != EOF)
        {
            if (c == '\n')
            {
                m++;
                n = 0;
            }
            else if (n >= len || m >= len)
            {
                fprintf(stderr, "Accessing array out of bounds at p[%d][%d]\n", m, n);
                exit(1);
            }
            else
                p[m][n++] = c;
        }
    }
    fclose(in);
    return 0;
}

static void showArea(int len, char p[len][len])
{
    for (int i = 0; i < len; i++)
    {
        for (int j = 0; j < len; j++)
            putchar(p[i][j]);
        putchar('\n');
    }
}

int main(void)
{
    int len = 12;
    char arr[len][len];
    memset(arr, '1', len * len);

    puts("Before:");
    showArea(len, arr);
    printArea(len, arr);
    scanArea(len, arr);
    puts("After:");
    showArea(len, arr);
}

When run on Mac OS X 10.10.1 (compiled with GCC 4.8.1), I get the output:

Before:
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
After:
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111
111111111111

All works, except my 8th line is completly blank, so now I got 13 lines instead of 12.

I can't account for your blank 8th line.

I note that the file name is repeated; repetition of things like file names leads to bugs. Ideally, the code should be revised so that the reading and writing functions are passed the name of the file to use, and the calling code should supply that name. However, that is a little beyond the scope of the immediate problem.

Also note that if showArea() was modified to take a file stream argument (void showArea(FILE *fp, int len, char p[len][len]) and the I/O calls were revised appropriately, then the code in printArea() could be revised to open the file, call showArea(), and then close the file.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • all works, except my 8th line is completly blank, so now i got 13 lines instead of 12 – s55michi Dec 15 '14 at 00:10
  • There is only one scanArea() and `m`, `n` and `c` are only used there – s55michi Dec 15 '14 at 00:22
  • Since you're working on Windows, that might be due to the carriage return before the newline. However, if the file is opened in text mode, which it is, that shouldn't be the issue. I can't reproduce the problem taking my code for `scanArea()` with your code for `printArea()` and assembling a simple `main()` and `showArea()`. – Jonathan Leffler Dec 15 '14 at 00:24
  • @chux: fair comment; I think it is the `return 0;` that's misplaced, but that misplacement leaves your observations correct. It leaks file streams, too. – Jonathan Leffler Dec 15 '14 at 00:43