Read last line of file first in C - 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;
}

Related

Attempting to read in a file and storing the first input in an array val and the second input in an array wt (weight)

I need to read in a file called "data.txt" and store the first input as a value and the second corresponding input as a weight. I'm having issues reading them in and storing the values.
data.txt (example)
3 25
2 20
1 15
4 40
5 50
This is what IĀ“ve started with:
FILE *myFile;
myFile=fopen("data.txt", "r");
int val[20]={0}; //initialize value array to zero
int wt[20]={0};
int W=80; //Set capacity to 80
int i;
int n;
while(!feof(myFile)){
fscanf(myFile, "%1d%1d", &val[i], &wt[i]);
}
n = sizeof(val)/sizeof(val[0]);
printf("%d", knapSack(W, wt, val, n));//prints out the maximum value
fclose(myFile);
return 0;
I've edited the above code to the following:
FILE *myFile;
myFile=fopen("data.txt", "r");
int val[20]={0};
int wt[20]={0};
int W=80; //Set capacity to 80
int i;
int n;
for(i=0;i<sizeof(val);i++){
fscanf(myFile, "%1d%1d", &wt[i],&val[i]);
}
n = sizeof(val)/sizeof(val[0]);
printf("%d", knapSack(W, wt, val, n));//prints out the maximum value
fclose(myFile);
return 0;
It keeps outputting 55 when I use the inputs from the data.txt example.
The biggest problem you are having is you are not controlling your read-loop with the return of the read itself. For example, in your case you would want:
int i = 0;
while (fscanf(myFile, "%1d%1d", &wt[i],&val[i]) == 2)
i++;
At the end of your read, i would hold the number of elements read into your arrays.
(note: you cannot use any input function correctly unless you check the return...)
Instead of reading the values into separate arrays, whenever you are coordinating multiple values as a single object (e.g. each val and wt pair), you should be thinking struct. That allows you to coordinate both values as a single object.
A simple example in your case could be:
#include <stdio.h>
#define MAXVAL 20 /* if you need a constant, #define one (or more) */
typedef struct { /* struct with int val, wt + typdef for conveninece */
int val, wt;
} mydata;
int main (int argc, char **argv) {
size_t n = 0; /* number of elements read */
mydata arr[MAXVAL] = {{ .val = 0 }}; /* array of mydtata */
/* 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;
}
/* read all pairs of values in file into array */
while (fscanf (fp, "%d %d", &arr[n].val, &arr[n].wt) == 2)
n++;
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (size_t i = 0; i < n; i++) /* output values */
printf ("arr[%zu] %2d %2d\n", i, arr[i].val, arr[i].wt);
}
Above, the code does the same as I suggested in conditioning the read-loop on successfully reading a pair of values from the file. The only difference is that is coordinates the val and wt values in a struct.
Example Use/Output
With your data in the file dat/val_wt.txt, you would receive the following output:
$ ./bin/read_val_wt dat/val_wt.txt
arr[0] 3 25
arr[1] 2 20
arr[2] 1 15
arr[3] 4 40
arr[4] 5 50
While above we read directly with fscanf, you can make your read a bit more robust by reading each line into a character array first, and then parsing the wanted values from the character array with sscanf. You are essentially doing the same thing, but by using fgets/sscanf you can make an independent validation of (1) the read of the line; and (2) the parse of the wanted information from the line. If you have a malformed-line, it prevents the matching-failure from impacting the read of the remaining lines in the input file.
Look things over and let me know if you have further questions.
Oops, many little problems here...
First even if unrelated, you consistenly fail to check the result of input functions. It can lead to hide problems...
Next, the rule is when you do not get what you would expect, trace intermediary values.
Had you happen those lines:
// uncomment next block for debugging
printf("n=%d\n);
for (i = 0; i < n; i++) {
printf("%d %d\n", wt[i], val[i]);
}
You would have seen
n = 20
3 2
5 2
2 0
1 1
5 4
4 0
5 5
0
showing that:
n was 20 (unsure whether you expected it)
you had read your values one digit at a time instead of one integer value (because of the %1d formats)
My advice:
for (i = 0; i<sizeof(val); i++) { // do not try to read more than array capacity
if (2 != fscanf(myFile, "%d%d", &wt[i], &val[i])) break; // stop when no more data
}
n = i; // number of actual values
// uncomment next block for debugging
/*
printf("n=%d\n);
for (i = 0; i < n; i++) {
printf("%d %d\n", wt[i], val[i]);
}
*/

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.

Weird behavior of ftell and fseek reading a txt file

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'

How to read a 10 GB txt file consisting of tab-separated double data line by line in C

I have a txt file consisting of tab-separated data with type double. The data file is over 10 GB, so I just wish to read the data line-by-line and then do some processing. Particularly, the data is layout as an matrix with, say 1001 columns, and millions of rows. Below is just a fake sample to show the layout.
10.2 30.4 42.9 ... 3232.000 23232.45
...
...
7.234 824.23232 ... 4009.23 230.01
...
For each line I'd like to store the first 1000 values in an array, and the last value in a separate variable. I am new to C, so it would be nice if you could kindly point out major steps.
Update:
Thanks for all valuable suggestions and solutions. I just figured out one simple example where I just read a 3-by-4 matrix row by row from a txt file. For each row, the first 3 elements are stored in x, and the last element is stored in vector y. So x is a n-by-p matrix with n=p=3, y is a 1-by-3 vector.
Below is my data file and my code.
Data file:
1.112272 -0.345324 0.608056 0.641006
-0.358203 0.300349 -1.113812 -0.321359
0.155588 2.081781 0.038588 -0.562489
My code:
#include<math.h>
#include <stdlib.h>
#include<stdio.h>
#include <string.h>
#define n 3
#define p 3
void main() {
FILE *fpt;
fpt = fopen("./data_temp.txt", "r");
char line[n*(p+1)*sizeof(double)];
char *token;
double *x;
x = malloc(n*p*sizeof(double));
double y[n];
int index = 0;
int xind = 0;
int yind = 0;
while(fgets(line, sizeof(line), fpt)) {
//printf("%d\n", sizeof(line));
//printf("%s\n", line);
token = strtok(line, "\t");
while(token != NULL) {
printf("%s\n", token);
if((index+1) % (p+1) == 0) { // the last element in each line;
yind = (index + 1) / (p+1) - 1; // get index for y vector;
sscanf(token, "%lf", &(y[yind]));
} else {
sscanf(token, "%lf", &(x[xind]));
xind++;
}
//sscanf(token, "%lf", &(x[index]));
index++;
token = strtok(NULL, "\t");
}
}
int i = 0;
int j = 0;
puts("Print x matrix:");
for(i = 0; i < n*p; i++) {
printf("%f\n", x[i]);
}
printf("\n");
puts("Print y vector:");
for(j = 0; j < n; j++) {
printf("%f\t", y[j]);
}
printf("\n");
free(x);
fclose(fpt);
}
With above, hopefully things will work if I replace data_temp.txt with my raw 10 GB data file (of course change values of n,p, and some other code wherever necessary.)
I have additional questions that I wish if you could help me.
I first initialized char line[] as char line[(p+1)*sizeof(double)] (note not multiplying n). But the line cannot be read completely. How could I assign memory JUST for one single line? What's the lenght? I assume it's (p+1)*sizeof(double) since there are (p+1) doubles in each line. Should I also assign memory for \t and \n? If so, how?
Does the code look reasonable to you? How could I make it more efficient since this code will be executed over millions of rows?
If I don't know the number of columns or rows in the raw 10 GB file, how could I quickly count rows and columns?
Again I am new to C, any comments are very appreciated. Thanks a lot!
1st way
Read file in chunks into preallocated buffer using fread.
2nd way
Map the file into your process memory space using mmap, move the pointer then over the file.
3rd way
Since your file is delimited by lines, open the file with fopen, use setvbuf or similar to set a buffer size greater than about 10 lines or so, then read the file line-by-line using fgets.
To potentially read the file even faster, use open with O_DIRECT (assuming Linux), then use fdopen to get a FILE * for the open file, then use setvbuf to set a page-aligned buffer. Doing that will allow you to bypass the kernel page cache - if your system's implementation works successfully using direct IO that way. (There can be many restrictions to direct IO)
Something to get you started: Reading 1 line
#define COLUMN (1000+1)
double data[COLUMNS];
for (int i = 0; i< COLUMN; i++) {
char delim = '\n';
int cnt = fscanf(in_stream, "%lf%c", &data[i], &delim);
if (cnt < 1) {
if (cnt == EOF && i == 0) return 0; // None read, OK as end of file
puts("Missing or bad data");
return -1; // problem
}
if (delim != '\t') {
// If tab not found, should be at end of line
if (delim == '\n' && i == COLUMN-1) {
return COLUMN; // Success
}
puts("Bad delimiter");
return -1;
}
}
puts("Extra data");
return -1;

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++;

Resources