Array dynamically allocated by file size is too large - c

I am working on a class assignment and need some help with dynamically allocated arrays. I am using file_size to try to pull the file size from 3 files to allocate the array to that size, then I need to write and sort the data in the array. My issue right now is with sizing of the array; right now my output (ignoring sorting) is:
1
3
7
9
0
0
0
0
2
4
8
0
0
0
5
6
10
0
0
0
0
0
0
As you can see it is being padded with extra 0s. Here are the input files:
inputFile1:
1
3
7
9
inputFile2:
2
4
8
inputFile3:
5
6
10
0
I need some help figuring out what's going on with this and where the issue is. I want to get rid of those extra 0s, and I'm not even sure where they are coming from. Help with the sorting would also be appreciated.
file_size:
long file_size(FILE *inputFile)
{
if(inputFile == NULL)
return -1;
long pos = ftell(inputFile);
fseek(inputFile, 0, SEEK_END);
long size = ftell(inputFile);
fseek(inputFile, pos, SEEK_SET);
return size;
}
Main:
int main(void)
{
FILE *file0 = fopen("list0.txt", "r");
FILE *file1 = fopen("list1.txt", "r");
FILE *file2 = fopen("list2.txt", "r");
FILE *output = fopen("hw3.out", "w");
long size0 = file_size(file0);
long size1 = file_size(file1);
long size2 = file_size(file2);
long totalSize = size0 + size1 + size2;
int *numbers = malloc(totalSize * sizeof(int));
int i;
int index = 0;
for(i = 0; i < file_size(file0); i++)
{
if(!feof(file0))
{
fscanf(file0, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < file_size(file1); i++)
{
if(!feof(file1))
{
fscanf(file1, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < file_size(file2); i++)
{
if(!feof(file2))
{
fscanf(file2, "%i", &numbers[index]);
index++;
}
else
break;
}
for(i = 0; i < totalSize; i++)
{
fprintf(output, "%i\n", numbers[i]);
}
fclose(file0);
fclose(file1);
fclose(file2);
fclose(output);
free(numbers);
return 0;
}

Your input files have several lines, each of which has the textual representation of a number. Your file size function however is counting the total number of bytes in the file. These are not the same.
While you can still use the file size to allocate space (you'll just get more than you need), you need to instead check the return value of scanf to see if a number was read. If not, you jump out of the loop.
int index = 0;
while (fscanf(file0, "%i", &numbers[index]) == 1) {
index++;
}
while (fscanf(file1, "%i", &numbers[index]) == 1) {
index++;
}
while (fscanf(file2, "%i", &numbers[index]) == 1) {
index++;
}
for(i = 0; i < index; i++)
{
fprintf(output, "%i\n", numbers[i]);
}

The size calculation includes the carriage return "\n" character at the end of the file. Therefore, you are allocating space for 8 integers for the first file, 6 integers for the second file and 10 integers for the third file (10, because the number "10" has two digits).
The correct allocation strategy would be not to count the bytes in the files, but the lines (which actually contain numbers, thus allowing you to skip empty lines).
But that's just too much trouble. Instead, consider just allocating 1000 bytes, read in until you run out, then re-alloc into a larger buffer.

Related

random numbers being read into array instead of the text file values

I am trying to make a function that reads all the numbers from a text file into an array, where each line of the file has a number, ex:
57346
40963
24580
98307
98312
32777
10
16392
16396
...
My function does allocate the necessary size to store the values, but the values being stored are random ones and 0's that aren't in my text file. Output ex:
0
0
296386
0
-485579776
-653048057
584
0
2095946880
...
This is my code:
typedef struct set{
void** values;
int size;
}Set;
int checkSize(FILE* file) {
int counter = 0;
char chr;
chr = getc(file);
while (chr != EOF) {
if (chr == '\n') {
counter = counter + 1;
}
chr = getc(file);
}
return counter;
}
Set* readSet(FILE* file){
Set* new = malloc(sizeof(Set));
new->size = checkSize(file);
new->values = malloc(sizeof(void*)*new->size);
int arrayAux[new->size];
int i = 0, n;
while(i < new->size) {
fscanf(file, "%ld", &arrayAux[i]);
new->values[i] = arrayAux[i];
i++;
}
//loop to remove the first three lines of the file, which are the number of values in the file,
//the biggest value of the file and the division between the number of values and the biggest value
for(i = 0; i < 3; i++) {
new->values[i] = new->values[i + 1];
new->size--;
}
for (i = 0; i <= new->size; i++) {
printf("%d\n", new->values[i]);
}
return new;
}
How can I fix this? Thanks in advance for any help.
Why void and not long?
You cannot do int arrayAux[new->size]; as size is a variable and thus cannot be used at compile time !!! 100% guarantee of reading out of bounds.
Read the value from file into a long and assign it to the proper space in your list.
Why have the size in every row? Use a global int
why loop to step over the first three in the list?
size -=3
i+=3
Works just as well

Not able to print this 2D array (weird output) in C

I am trying to read a text file with 100 numbers like 1 2 45 55 100 text file here (all on a single line) and then put them in a 10x10 array (2D array).
736.2 731.6 829.8 875.8 568.3 292.2 231.1 868.9 66.7 811.9 292.0 967.6 419.3 578.1 322.5 471.7 980.0 378.8 784.1 116.8 900.4 355.3 645.7 603.6 409.1 652.1 144.1 590.6 953.1 954.0 502.0 689.3 685.6 331.9 565.1 253.9 624.1 796.2 122.8 690.7 608.0 414.8 658.3 27.3 992.9 980.8 499.0 972.8 359.7 283.1 89.7 260.1 638.4 735.4 863.6 47.5 387.5 7.7 638.1 340.6 961.7 140.1 29.8 647.3 471.9 594.9 901.2 96.0 391.1 24.0 786.7 999.1 438.7 445.0 26.4 431.6 425.9 525.4 404.4 785.6 808.5 494.1 45.7 447.0 229.5 909.3 494.4 617.0 917.0 132.5 957.5 878.8 272.6 987.4 526.1 744.5 582.3 427.3 840.5 973.3
Here is my code:
#include <stdio.h>
#define NR 10
#define NC 10
int main(void) {
int numbers[9][9];
int i = 0;
int count;
int j = 0;
FILE *file;
file = fopen("numbers.txt", "r");
for (count = 1; count < 101; count++) {
fscanf(file, "%d", &numbers[i][j]);
j++;
if ((count != 1) && (count % 10 == 0)) {
i++;
j = 0;
}
}
fclose(file);
int p = 0;
int q = 0;
for (p = 0; p < NR; p++) {
for (q = 0; q < NC; q++) {
printf("%d", numbers[p][q]);
}
printf("\n");
}
return 0;
}
As SparKot noted in a comment, to read a 10x10 matrix, you need to define the matrix with 10x10 elements:
int numbers[10][10];
That has to be one of the weirder ways of reading a 10x10 matrix that I've ever seen. Why not go for a simple approach of nested loops. Since the data contains floating-point numbers, you need to read them as double (or perhaps float) values.
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
double double_val;
if (fscanf(file, "%lf", &double_val) != 1)
{
fprintf(stderr, "failed to read matrix[i][j]\n", i, j);
exit(EXIT_FAILURE);
}
numbers[i][j] = double_val;
}
}
The mess with double_val works around the data containing floating point numbers and your original code trying to read integers. You'll get one valid value; thereafter, fscanf() will return 0 because the . is not a part of a valid integer. This highlights the importance of checking the return value from fscanf() and its relatives.
Frankly, you should be using double numbers[10][10]; for the data from the file. Then you could read directly into the array:
if (fscanf("%lf", &numbers[i][j]) != 1)
But you'd need to check (and probably change) all the rest of the code too.
There are multiple issues in your code:
the matrix is too small, make it numbers[NR][NC].
you do not check for fopen failure: you will have undefined behavior if the file numbers.txt is not in the current directory or cannot be open for reading.
you read the file contents as integers, but the file contains floating point numbers with a . decimal separator: the second and subsequent fscanf() will get stuck on the . and keep returning 0 without modifying the destination number, leaving the matrix mostly uninitialized. Make the matrix double numbers[NR][NC], read the numbers with %lf and test for conversion failure.
the counting method in the reading loop is weird. Just use 2 nested for loops with proper counter and tests.
printing the matrix contents, you should output at least a space between numbers so the output is readable.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define NR 10
#define NC 10
int main() {
double numbers[NR][NC];
FILE *file;
file = fopen("numbers.txt", "r");
if (file == NULL) {
fprintf(stderr, "cannot open numbers.txt: %s\n", strerror(errno));
return 1;
}
for (int i = 0; i < NR; i++) {
for (int j = 0; j < NC; j++) {
if (fscanf(file, "%lf", &numbers[i][j]) != 1) {
fprintf(stderr, "error reading number at row %d, col %d\n",
i + 1, j + 1);
fclose(file);
return 1;
}
}
}
fclose(file);
for (int p = 0; p < NR; p++) {
for (int q = 0; q < NC; q++) {
printf(" %5g", numbers[p][q]);
}
printf("\n");
}
return 0;
}
Clear all a common condition that causes programs to crash; they are often associated with a file named core.
code is showing segmentation fault.

Reading multiple files with different number of lines

I am trying to read arrays from multiple files, and the array size in each file is different. So what I do is, I try to count the number of lines in the file and then store that as the array size.
For example, I have two .txt files, File_1.txt and File_2.txt which contain the following data:
0.000 300.00
0.054 2623.3
1.000 300.00
0.000 300.00
0.054 2623.3
0.500 1500.0
1.000 300.00
respectively.
Here is the code that I use:
int main()
{
char filter[1024];
char filename[60];
FILE *fp;
double *T_SR, Z_SR;
for (int i = 1; i < 3; i++)
{
sprintf(filename, "File_%d.txt", i);
fp = fopen(filename, "r");
if (fp == NULL)
{
exit(1);
}
int count = 0;
for (int j = getc(fp); j != EOF; j = getc(fp))
{
if (j == '\n')
{
count = count + 1;
}
}
T_SR = (double *)malloc(count * sizeof(double));
Z_SR = (double *)malloc(count * sizeof(double));
for (int rows = 0; rows < count; rows++)
{
fscanf(fp, "%lf %lf", &Z_SR[rows], &T_SR[rows]);
printf("%lf %lf\n", Z_SR[rows], T_SR[rows]);
if (feof(fp))
{
break;
}
}
}
}
But instead of printing the given array as output, it prints this:
0.0000 0.0000
0.0000 0.0000
I checked the value of count, it's good. Maybe the problem is simple, but I am not able to find it. Can someone please help?
After you ran the whole file with getc the file indicator will be at the end of the file you must set it back to the beginning before you use fscanf, you can use rewind for that.
rewind(fp); //<--
for (int rows = 0; rows < count; rows++)
{
//...
}
Aside from that, other problems exist as Jaberwocky pointed out, among others, like a memory leak issue, and the fact that you don't close your files or check malloc return, here's how your code could look like (with comments):
double *T_SR, *Z_SR; // fix the pointer issue
//...
char line[1024]; // make sure it's larger than the largest line in the file
while (fgets(line, sizeof line, fp)) // fixes the count issue
{
// doesn't count empty lines, if there are any
if (line[0] != '\n')
{
count++;
}
}
if(count > 0)
{
T_SR = malloc(count * sizeof *T_SR);
Z_SR = malloc(count * sizeof *Z_SR);
if(T_SR == NULL || Z_SR == NULL) // check memory allocation
{
perror("malloc");
return EXIT_FAILURE;
}
rewind(fp);
for(int rows = 0; fscanf(fp, "%lf%lf", &Z_SR[rows], &T_SR[rows]) == 2; rows++)
{
printf("%lf %lf\n", Z_SR[rows], T_SR[rows]);
}
free(T_SR); // free the memory, avoids memory leaks
free(Z_SR);
}
fclose(fp); // and close the file
//...
Live demo
There are several bugs:
The most important one is the rewind issue that has been addressed in anastaciu's anwer.
double * T_SR, Z_SR is wrong, it should be double * T_SR, *Z_SR. I wonder actually if the code you posted is the code you compile.
your line counting method is flawed. If the last line of the file does not end with a \n, the count variable will be 2 and you'll miss the last line.
fscanf returns the number of items read or EOF. If you had check that, you might have found the problem in your code yourself.
the feof check is done too late, if fscanf encounters en EOF you still print the values that have not bee read due to the EOF condition.
I try to count the number of lines in the file and then store that as the array size.
Aside from the key rewind() issue, avoid reading code one way to find line count and another to find the doubles. Far too easy to get a line count that does not match the "line count" of reading two doubles.
Use one approach to find both.
size_t read_SR(size_t count, double *Z_SR, double *T_SR, FILE *inf) {
char line[100];
rewind(inf);
size_t rows;
while (fgets(line, sizeof line, inf)) {
double Z, T;
if (sscanf(line, "%lf %lf", &Z, &T) != 2) return rows;
if (rows < count) {
if (Z_SR) Z_SR[rows] = Z;
if (T_SR) T_SR[rows] = T;
}
rows++;
}
return rows;
}
Usage
// First pass, find size
size_t count = read_SR(0, NULL, NULL, inf);
double *T_SR = malloc(sizeof *T_SR * count);
double *Z_SR = malloc(sizeof *Z_SR * count);
// 2nd pass, save data
read_SR(count, Z_SR, T_SR, inf);

Scan set of integers into 2-D array

Consider the following set of numbers:
10 20 30
11 31 45
...
I want to define a 2-D array int_array[][3] such that int[0] contains {10, 20, 30}, then int[1] contains {11, 31, 45} and so on.
Assume that these values are in a file. The code I would use is therefore:
int int_array[NUM_ROWS][3], i;
for (i=0; i<NUM_ROWS;i++)
fscanf(filePointer, "%d", int_array[i]);
However, this produces ridiculous results. What am I doing wrong?
produces ridiculous results. What am I doing wrong?
int_array[i] points to only 1 int, not the 3 needed.
Code lacks error checking.
Create a helper function to read 1 line of data, saving the data when d != NULL.
int read3int(FILE *inf, int d[3]) {
char buf[100];
if (fgets(buf, sizeof buf, inf) == NULL) {
return EOF;
}
int n = 0;
int i[3];
sscanf(buf, "%d%d%d %n", &i[0], &i[1], &i[2], &n);
// Was scan incomplete or with extra junk?
if (n == 0 || buf[n] != 0) {
return 0;
}
if (d) {
d[0] = i[0];
d[1] = i[1];
d[2] = i[2];
}
return 1;
}
To form the array, first find how many rows.
size_t rows = 0;
while (read3int(filePointer, NULL) > 0) {
rows++;
}
// Form the array
int data[rows][3]; // VLA or use malloc
// Rewind !!
rewind(filePointer);
for (size_t r = 0; r < rows; r++) {
if (read3int(filePointer, data[r]) < = 0) {
// Unexpected 2nd pass inconsistency
break;
}
}
You said in the comments: "my real set of numbers does not have 3 columns but instead multiple"
I am posting a general method to read lines of integers from a file, storing them in a 2D array. It works regardless of the number of integers in each line.
But I haven't run this piece of code and I never used strtol, so chances are it doesn't work. I am giving you the idea, you can implement it properly.
char line[N], *ptr;
for(i = 0; fgets(line, N, filePointer) != NULL; i++) {
int_array[i][0] = (int) strtol(line, &ptr, 10);
for(j = 1; *ptr != '\n'; j++) {
int_array[i][j] = (int) strtol(ptr, &ptr, 10);
}
}
The logic is to read an entire line as a string and then convert the numbers from char to int.

How to fix error of fscanf(), checking if an element in matrix is an integer or not in C

I am tasked with reading a matrix from a text file and find out if it is a magic square, I also have to make sure that all the entries of the matrix are integers and if not I have to write to the standard output an ERROR message while also describing the position where this error occurred.
Now, I succeeded in reading all the elements of matrix but I have a problem with the verification if it is an integer or not.
I used fscanf() because I know that on success it returns the number of elements it succeeded reading correctly and returns EOF or 0 otherwise.
Here is the matrix:
3
12 3 87
78 a 9
45 0 23
first element is the number of rows and columns.
and here is my code ( the part I need help with at least )
while(!feof(file))
{
while ( fgets(buffer, sizeof(buffer), file) )
{
for ( int j = 0; j < n; j++ )
{
if ( i == n )
{
break;
}
m = fscanf(file, "%d", &a[i][j]);
printf("\n%d", m);
if ( m == 0 || m == EOF )
{
printf("\nERROR, %d, %d, %d \n", i, j, a[i][j]);
}
}
i++;
}
}
and here is my output ( right now I'm just trying to figure out this problem so this is not how it will look later on ):
1
1
1
1
0
ERROR, 1, 1, 0
0
ERROR, 1, 2, 48
1
1
1
12 3 87
78 0 48
45 0 23
When I replace 'a' with a number of type (float) for example it will display the ERROR message just for a[1][1] BUT it will replace 9 with 48.
When I have a character ( like in this case ) it will display an ERROR message both for a[1][1] ('a') and a[1][2] ('9') and replace it too.
And my question is why does this happen and what the hell happens between a[1][1] and a[1][2] and how to fix it.
EDIT:I know I could simply exit the program when I find 1 case but I'm really curious on why does this error happen.
fscanf(file, "%d", &n );//I read the first integer, which is also the maximum capacity of my 2d array
int a[n][n];
First points:
while(!feof(file))
{
You will want to look at Why is while ( !feof (file) ) always wrong?. Further, the entire outer loop is superfluous to your code and can simply be removed.
With any of the scanf family of functions, when input does not match the conversion specifier used in the format string, a matching failure occurs, character extraction from the input stream stops at the point of failure, the offending characters causing the failure are left in the input stream (unread) just waiting to bite you on your next attempted read. Attempting to read 'a' as type int with scanf produces a matching failure.
How To Handle A Matching Failure?
Since you always validate all user input, as you note, you detect an input failure with scanf when the return is less than the number of conversions specified (or EOF). Reading an integer at a time with fscanf within your loops tells you exactly where the input failure happened from a row/col standpoint when reading a square matrix worth of data. Knowing where the failure occurred you can output the row/col where the invalid data was encountered.
To capture the invalid data for reporting purposes, you can simply scan forward with fgetc reading a character at a time and filling a buffer with the offending characters until the next valid digit or +/- (explicit sign for a digit) is encountered (you can use ungetc at that point to put the valid digit (or sign) back in the input stream if your wanted to continue reading additional values)
A Short Example
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXC 1024
int main (int argc, char **argv) {
int **m = NULL;
unsigned dim = 0;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (fscanf (fp, "%u", &dim) != 1) { /* read square mtrx dim */
fputs ("error: invalid format, no dimension.\n", stderr);
return 1;
}
if (!(m = malloc (dim * sizeof *m))) { /* allocate/validate dim ptrs */
perror ("malloc-m");
return 1;
}
for (unsigned i = 0; i < dim; i++) /* allocate dim rows of dim int */
if (!(m[i] = calloc (dim, sizeof *m[i]))) { /* zero mem w/calloc */
perror ("calloc-m[i]");
return 1;
}
for (unsigned i = 0; i < dim; i++) /* for each row */
for (unsigned j = 0; j < dim; j++) { /* for each col */
if (fscanf (fp, "%d", &m[i][j]) != 1) { /* read/validate int */
char buf[MAXC], *p = buf; /* buf and ptr to buf */
int c; /* int for char */
/* read while !EOF, not digit and not -/+ */
while ((c = fgetc(fp)) != EOF && (c < '0' || '9' < c) &&
c != '-' && c != '+')
if (!isspace(c)) /* if not a space */
*p++ = c; /* store offending char(s) */
*p = 0; /* nul-terminate buf */
if (c != EOF) /* if c a char - put it back */
ungetc(c, fp);
/* output location of invalid input */
printf ("error: m[%d][%d] - invalid entry '%s'.\n",
i, j, buf);
return 1; /* and bail - or continue -- your choice */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (unsigned i = 0; i < dim; i++) { /* for each row */
for (unsigned j = 0; j < dim; j++) /* for each col */
printf (" %3d", m[i][j]); /* output value */
putchar ('\n'); /* output newline */
free (m[i]); /* free ints in row */
}
free (m); /* free pointers */
return 0;
}
(note: you didn't say how you allocated storage, so a simple pointer to pointer to int was used to allocate dim pointers and a block of dim integers was allocated and the starting address for each allocation assigned to each of the pointer to allow indexing as a 2D array)
Example Input Files
Using your example input with invalid int at [1][1]:
$ cat ~/tmpd/mtrx.txt
3
12 3 87
78 a 9
45 0 23
An example with good values:
$ cat ~/tmpd/mtrxgood.txt
3
12 3 87
78 8 9
45 0 23
Example Use/Output
With invalid int at [1][1]:
The program correctly reports the location and the offending character:
$ ./bin/readsquaremtrx ~/tmpd/mtrx.txt
error: m[1][1] - invalid entry 'a'.
With good values, all values are read and the matrix printed to the screen before all allocated memory is freed:
$ ./bin/readsquaremtrx ~/tmpd/mtrxgood.txt
12 3 87
78 8 9
45 0 23
The code is commented to help you follow along. If you have any questions, just let me know.
Key Issue of your Code: Your FILE pointer doesn't move ahead when fscanf encounters an error. You need to move it ahead using fseek.
Some of the issues you need to look at:
How to allocate an array of sufficient size to read all inputs correctly
File based error handling and how to move the FILE pointer ahead if it hits an error while reading using fscanf. My below code considers
only the specific case of a single incorrect character being present,
you will have to handle it appropriately if more than one character is
present.
There is no need for you to do both fgets and fscanf. Only one is sufficient.
Relook, if you really want to use fscanf. The same logic can be achieved using other read methods related to FILE.
Treat the below code strictly as a pseudo code, just giving some indications. This doesn't handle all conditions at all. You need to work on lot of aspects of your code and handle additional error conditions etc. I have just given a pointer on how you can solve it.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *f = fopen("./check.txt", "r");
int a[3][3] = {0};
int n, res, i;
res = fscanf(f, "%d", &n);
i = 0;
while(i<n)
{
for(int j = 0; j < n; j++)
{
int val = 0;
res = fscanf(f, "%d", &val);
if ((res == 0) || (res == EOF))
{
printf("Error: res = %d for i = %d, j = %d, val = %d\n", res, i, j, val);
fseek(f, 1, SEEK_CUR);
}
else
a[i][j] = val;
}
i++;
}
printf("The Matrix\n");
for(int i = 0; i<n; i++)
{
for (int j=0; j<n; j++)
printf("%d ", a[i][j]);
printf("\n");
}
}
Output:
Error: res = 0 for i = 1, j = 1, val = 0
The Matrix
12 3 87
78 0 9
45 0 23
Another example using a flat memory region instead of a somwhat inefficient "array" of pointers to pointers:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
int is_magic_square(int *square, size_t dimension)
{
if(dimension < 3) {
return 0;
}
int prev_row_sum;
int prev_col_sum;
int d1_sum = 0;
int d2_sum = 0;
for (size_t y = 0; y < dimension; ++y) {
int curr_row_sum = 0;
int curr_col_sum = 0;
for (size_t x = 0; x < dimension; ++x) {
curr_row_sum += square[y * dimension + x];
curr_col_sum += square[x * dimension + y];
if (x == y) {
d1_sum += square[y * dimension + x];
d2_sum += square[y * dimension + dimension - 1 - x];
}
}
if (y && (curr_row_sum != prev_row_sum || curr_col_sum != prev_col_sum)) {
return 0;
}
prev_row_sum = curr_row_sum;
prev_col_sum = curr_col_sum;
}
return prev_row_sum == d1_sum && prev_row_sum == d2_sum ? prev_row_sum : 0;
}
int main(void)
{
char const *filename = "test.txt";
FILE *input = fopen(filename, "r");
if (!input) {
fprintf(stderr, "Couldn't open \"%s\" for reading :(\n\n", filename);
return EXIT_FAILURE;
}
size_t dimension;
if (fscanf(input, " %zu", &dimension) != 1) {
fprintf(stderr, "Faild to read squares dimensions from \"%s\" :(\n\n", filename);
fclose(input);
return EXIT_FAILURE;
}
int result = EXIT_FAILURE;
printf("Reading a %zu x %zu square from \"%s\" ...\n\n", dimension, dimension, filename);
int *square = calloc(dimension * dimension, sizeof(*square));
if (!square) {
fputs("Not enough memory :(\n\n", stderr);
goto cleanup;
}
for (size_t y = 0; y < dimension; ++y, putchar('\n')) {
for (size_t x = 0; x < dimension; ++x) {
int value;
if (fscanf(input, " %d", &value) != 1 || value < 1) {
fprintf(stderr, "\n\nFailed to read value at (%zu, %zu) from \"%s\" :(\n\n",
x + 1, y + 1, filename);
goto cleanup;
}
for (size_t pos = 0; pos < y * dimension + x; ++pos) {
if (square[pos] == value) {
fprintf(stderr, "\n\nDuplicate value %d found at (%zu, %zu) in \"%s\" :(\n\n",
value, x + 1, y + 1, filename);
goto cleanup;
}
}
if(value > dimension * dimension) {
fprintf(stderr, "\n\nValue %d at (%zu, %zu) in \"%s\" is out of range 1, 2, ..., %zu^2 :(\n",
value, x + 1, y + 1, filename, dimension);
goto cleanup;
}
printf("%4d ", value);
square[y * dimension + x] = value;
}
}
int sum = is_magic_square(square, dimension);
printf("\nThis %s a perfect square!\n", sum ? "is" : "is not");
if (sum) {
printf("It's sum is %d.\n", sum);
}
putchar('\n');
result = EXIT_SUCCESS;
cleanup:
free(square);
fclose(input);
return result;
}
Sample Input:
4
10 3 13 8
5 16 2 11
4 9 7 14
15 6 12 1
Sample Output:
Reading a 4 x 4 square from "test.txt" ...
10 3 13 8
5 16 2 11
4 9 7 14
15 6 12 1
This is a perfect square!
It's sum is 34.

Resources