Weird behavior of ftell and fseek reading a txt file - c

I'm trying to read a txt file containing a "picture" (a matrix of chars) and ftell and fseek seem to be working in a different way than what i was taught in class and on the web.
Here is the code:
fp = fopen(filename, "r");
checkFopen(fp);
findRowsAndCols(fp, &rows, &cols);
for (i = 0; i < cols; i++)
{
fseek(fp, i, SEEK_SET);
for (j = 0; j < rows; j++)
{
ch = fgetc(fp);
if (ch != ' ')
{
//doing something with ch..
}
test1 = fseek(fp, (cols-1), SEEK_CUR);
test2 = ftell(fp);
}
}
//cols = 9 (int)
Instead of jumping 8 characters in the file the cursor is moved only by 3
test1 is always 0 while test2 is increased twice by 4 than by 5 and by 6..
This is a very weird behavior for a program in my opinion. What am I doing wrong?
Edit:
the text file:
12345678\n
9!##____\n
________\n
________\n
________\n
I'm trying to read the file char by char going through each column from top to bottom.
The first char I get is '1' then '5' and then '\n'

Related

Taking input and output to file using pointer array

Here, the array pointer name[i] is not not working and I can't figure out how to change the pointer or the array to make it work. There was no compiler error/warning but the console terminal showed a negative return value and printf() was not working.
Also, the output file was created but it was empty: nothing was written into it.
I guess there's some problem with the fscanf() action of taking string input from file.
#include<stdio.h>
#include<math.h>
int main()
{
FILE *fr, *fw;
fr = fopen("data.txt", "r");
float wt[10], ht[10], bmi[10];
char *name[5];
for(int i = 0; i < 5; i++)
{
fscanf(fr, "%s\t%f\t%f", &name[i], &wt[i], &ht[i]);
bmi[i] = wt[i] / pow(ht[i], 2);
}
printf("%s", name[2]);
fw = fopen("bmi.txt", "w");
for(int j = 0; j < 5; j++)
{
fprintf(fw, "%s\t%f\n", name[j], bmi[j]);
}
fclose(fr);
fclose(fw);
return 0;
}
You should not assume that the file size is 5. You should dynamically allocate memory to the name variable like so:
int length;
fseek(filename, 0, SEEK_END);
length = ftell(filename);
name = malloc(length);
fread(name, 1, length, filename);

Manipulating files through C to make a top 5 leaderboard

I'm making a connect 4 game as apart of a project and I want to add extra functionality to it, however I'm struggling.
The code is a function I've written that takes a struct (lb leaderboard) and attempts to read the file for that difficulty of all its inputs into a lb array size of 6 which include the difficulty of the bot, turn count and the name of the player, then organizes it in order of turn count and reprints the top 5 back into the file.
I set the first element of the old array to the passed by struct of leaderboard data for the organise function bit.
However, when i set it up with template inputs each separated with a new line like such:
0 7 James
0 13 Jimmy
0 8 Joshua
0 6 Charly
0 9 Jack
It prints this to the file:
0 7 James
0 13 Jimmy
0 8 Joshua
0 6 Charly
(0 9 Jack 1982289408 0 ) same line
The struct looks like this as well{
int turn_count
int diff
char name[20]}
I'm really new to coding so any help is appreciated!
lb old[6];
lb temp;
FILE *file;
if (leaderboard.diff == 0){
file = fopen("leaderboard0.txt", "r+");}
else if (leaderboard.diff == 1){
file = fopen("leaderboard1.txt", "r+");}
else if (leaderboard.diff == 2){
file = fopen("leaderboard2.txt", "r+");}
fseek( file, 1, SEEK_SET );
old[0] = leaderboard;
for (int i = 1; i < 6; i++){
fscanf(file, "%i %i %s", old[i].diff, old[i].turn_count, old[i].name);}
for (int i = 0; i < 6; i++){
for (int j = i + 1; j < 6; j++)
if (old[i].turn_count > old[j].turn_count)
{
temp = old[i];
old[i] = old[j];
old[j] = temp;
}}
for (int i = 0; i < 5; i++){
fprintf(file, "%i %i %s\n", old[i].diff, old[i].turn_count, old[i].name);
fclose(file);}
}
Welcome to StackOverflow, JJewson
First of all, I strongly recommend to you to properly indent your code. One error will be obvious only by that:
lb leaderboard = { ... }; /* was missing from your source code */
lb old[6]; /* you should rename this */
lb temp;
FILE *file;
/* somthing that opens a block was missing from your example source */ {
if (leaderboard.diff == 0) {
file = fopen("leaderboard0.txt", "r+");
} else if (leaderboard.diff == 1) {
file = fopen("leaderboard1.txt", "r+");
} else if (leaderboard.diff == 2) {
file = fopen("leaderboard2.txt", "r+");
}
fseek( file, 1, SEEK_SET );
old[0] = leaderboard;
for (int i = 1; i < 6; i++) {
fscanf(file, "%i %i %s", old[i].diff, old[i].turn_count, old[i].name);
}
for (int i = 0; i < 6; i++) {
for (int j = i + 1; j < 6; j++) {
if (old[i].turn_count > old[j].turn_count) {
temp = old[i];
old[i] = old[j];
old[j] = temp;
}
}
}
for (int i = 0; i < 5; i++) {
fprintf(file, "%i %i %s\n", old[i].diff, old[i].turn_count, old[i].name);
fclose(file); /* this is the obvious error, it should go below this loop */
}
fclose(file); /* here it should be */
}
It would be probably best to rename leaderboard to leader and after that to rename old to leaderboard.
I already mentioned the need to move the fclose call outside of the loop. You close the file after your first write.
I don't understand your first use of fseek( file, 1, SEEK_SET );: Why do you skip the first character of the file? (File offsets start at 0 not 1). Also: After a successfull fopen, the file pointer always points to the beginning of the file.
After you read your leaderboard, you should fclose the file, and reopen it for (over-)writing. (Better keep a backup, if the leader board is important). So: Open "r" for reading, read, close, open "w" for writing, write, close.
You don't do any error checking in your code. You should fix that and inform the user on error and try to handle the error as gracefully as possible. (I.e. do not loose your leaderboard if writing fails.)
Concerning the added part 1982289408 0: Make sure, your new leader (struct `leaderboardĀ“) is properly initialized).
Your algorithm has a slight problem: A new player, who was exactly as good as a previous one, will move that previous one out of the leader board. To fix that, load the file into old[0..4] and put new leader into old[5] (instead of putting him in old[0] and the file content in old[1..5])
Minor imporovement: The sorting loop: for (int i = 0; i < 6; i++) for (int j = i + 1; j < 6; j++)-> thefor iloop only needs to run from 0 .. 4, sofor (int i = 0; i < 5; i++)` would suffice.

Array dynamically allocated by file size is too large

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.

How to read lines from a text file and store as char array?

I have an input text file with some instructions and, starting with line 7, several lines of text. Something like this:
hi gBroThuo oWdbmna eo mt ce oneain,nDustuh o n
Ade ds,bpopoonf oneigno abro wmt eIw
n,Yrtyt hlil t .s Ble a meyboefr rtIhoyod
wla rimw yidehl. kes ,oyn L af
fu;AcMadmdnae nddmh ita behsctr rft iHdo"l,sie g"hu!,n eoaecMBt-
- h
I need to store the text to be stored in a char array (including the new line characters). What functions can I used to read and store this text to a single char array?
char fileBuf[MAXFILE];
FILE *f;
int c;
size_t i;
if (f = fopen("filename", "r")) {
for (i = 0; i < (MAXFILE - 1) && (c = getc(f)) != EOF; ++i)
fileBuf[i] = c;
fclose(f);
} else perror("Could not open file");
EDIT: you said you wanted to skip the first 7 lines.
int x;
char line[MAXLINE];
for (x = 0; x < 7; ++x)
fgets(line, MAXLINE, f); /* skips first 7 lines, so now file pointer
will point to data after the 7 lines */
You could do something like this:
int i = 0;
while(fscanf(inputFile, %c, &charArray[i])!=EOF)
i++;

Read last line of file first in C

I currently have a file that appends new entries to the current file. I would like to fetch the 5 most recent entries. How can I read the last line first in C? I would like to use the fgets command to read in line by line if that's possible.
Thank you for your help!
Edit:
For instance:
Original File:
The cat is fast.
Dogs are cool.
I like pie.
Desired Output:
I like pie.
Dogs are cool.
The cat is fast.
while(fgets(buffer,sizeof(buffer),fp); //go on scanning lines
//Now `buffer` holds the last line of `fp`
#include <stdio.h>
#define N 5 //number of recent entry
int main(void){
long entry[N+1];//+1 for end of file
int i, index = 0;
FILE *fp = fopen("entry.txt", "r");
char line[128];
for(i=0;i<N+1;++i)
entry[i] = -1L;//initialize to invalid value
do{ //path I : store file position
entry[index++] = ftell(fp);
if(index == N+1)
index = 0;
}while(EOF!=fscanf(fp, "%*[^\n]%*c"));
if(--index < 0)//one back index
index += N+1;
entry[index] = -1L;//for end of file
for(i = 0; i < N; ++i){//get N entry
if(--index < 0)
index += N+1;
if(entry[index] < 0L)
break;//when number of entry < N
fseek(fp, entry[index], SEEK_SET);
fgets(line, sizeof line, fp);
fputs(line, stdout);
}
fclose(fp);
return 0;
}

Resources