read the last N lines from a file - c

I am trying to get this piece of code to read a line from a file but it's not working. I was wondering if one of you could help me. It was going to read the last 5 lines which I can configure later, but right now I am just trying to get it to read the last line.
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main() {
FILE *myfile = fopen("X:\\test.txt", "r");
int x, number_of_lines = 0, count = 0, bytes = 512, end;
char str[256];
do {
x = fgetc(myfile);
if (x == '\n')
number_of_lines++;
} while (x != EOF); //EOF is 'end of file'
if (x != '\n' && number_of_lines != 0)
number_of_lines++;
printf("number of lines in test.txt = %d\n\n", number_of_lines);
for (end = count = 0; count < number_of_lines; ++count) {
if (0 == fgets(str, sizeof(str), myfile)) {
end = 1;
break;
}
}
if (!end)
printf("\nLine-%d: %s\n", number_of_lines, str);
fclose(myfile);
system("pause");
return 0;
}

Here is a simple solution where you read all lines into a circular line buffer and print the last 5 lines when the end of file has been reached:
#include <stdio.h>
int main(void) {
char lines[6][256];
size_t i = 0;
FILE *myfile = fopen("X:\\test.txt", "r");
if (myfile != NULL) {
while (fgets(lines[i % 6], sizeof(lines[i % 6]), myfile) != NULL) {
i++;
}
fclose(myfile);
for (size_t j = i < 5 ? 0 : i - 5; j < i; j++) {
fputs(lines[j % 6], stdout);
}
}
return 0;
}

Just make a for or while cycle that reads all the file(use fscanf) and when the reading gets to your desired line you save it to a var.

Related

how to print line numbers from multiple files without using fgets

I'm trying to print line numbers in the beginning of the lines without using fgets()
yes, it prints line number well when I input multiple files
but I want to get result like this. Can you guys help me with this?
Now result
1 I'll always remember
2 the day we kiss my lips
3
4 light as a feather
*5 #####localhost ~ $*
expect result
1 I'll always remember
2 the day we kiss my lips
3
4 light as a feather
*#####localhost ~$*
Here is my code:
#include <stdio.h>
int main(int argc, char *argv[]) {
FILE *fp;
int c, n;
n = 1;
for (int i = 1; i < argc; i++) {
if (argc < 2)
fp = stdin;
else
fp = fopen(argv[i], "r");
c = getc(fp);
printf("%d ", n);
while (c != EOF) {
putc(c, stdout);
if (c == '\n')
n++, printf("%d ", n);
c = getc(fp);
}
fclose(fp);
}
return 0;
}
Do not write printf("%d ", n); when you do not know if there is the next line. Or, otherwise, do printf("%d ", n); only on the beginning of the file and after a newline when you know there is a next char.
#include <stdbool.h> // for bool, true, false
bool previous_character_was_a_newline = true;
while ((c = getc(fp)) != EOF) {
if (previous_character_was_a_newline) {
previous_character_was_a_newline = false;
printf("%d ", n);
}
putc(c, stdout);
if (c == '\n') {
n++;
previous_character_was_a_newline = true;
}
}
Do not write code like n++, printf("%d ", n);, it will be confusing. Strongly prefer:
if (c == '\n') {
n++;
printf("%d ", n);
}
Your implementation outputs the line number before the first line and after each newline, including the one at the end of the file. This causes an extra line number to appear at the end of the file.
Let's define the output more precisely:
you want the line number at the beginning of each line, no output if no line, no line number after the last line.
do you want the line counter to reset to 1 when a new file is read? I assume no, but cat -n does.
do you want to output an extra newline at the end of a non empty file that does not end with a newline? I assume yes but cat -n does not.
Here is a modified version where the answer is no for the first question and yes for the second:
#include <stdio.h>
int output_file(FILE *fp, int line) {
int c, last = '\n';
while ((c = getc(fp)) != EOF) {
if (last == '\n') {
printf("%d\t", line++);
}
putchar(c);
last = c;
}
/* output newline at end of file if non empty and no trailing newline */
if (last != '\n') {
putchar('\n');
}
return line;
}
int main(int argc, char *argv[]) {
int n = 1;
if (argc < 2) {
n = output_file(stdin, n);
} else {
for (int i = 1; i < argc; i++) {
FILE *fp = fopen(argv[i], "r");
if (fp == NULL) {
perror(argv[i]);
} else {
n = output_file(fp, n);
fclose(fp);
}
}
}
return 0;
}

Why are there extra characters in the output of the following C code?

I have file statistics.txt where is the following data:
Mark = 100
Andy = 200
Then, I wrote this code:
FILE *file_statistics_reading = fopen("statistics.txt", "r");
char line[1024];
int n = 0;
char player[10];
while (fgets(line, sizeof(line), file_statistics_reading) != NULL) {
n = 0;
for (int i = 0; i < 10; i++) {
if ((line[i] > 'A') && (line[i] < 'z')) {
player[n] = line[i];
n = n + 1;
}
}
printf("%s", player);
}
fclose(file_statistics_reading);
I want to extract the names of the players from the text file and print them out, but the output looks like this:
Mark╠╠╠╠╠╠╠╠╠╠╠╠╠
Andy╠╠╠╠╠╠╠╠╠╠╠╠╠
Any solutions?
There are multiple problems in the code:
You forgot to set a null terminator after the name in player, which explains the random bytes in the output. player is an automatic array: its contents are indeterminate at creation time.
You should make player one byte longer.
The test for letters is incorrect: 'A' and 'z' will cause the loop to stop because you use > and < instead of >= and <=
Depending on the character set, some non letter bytes will get printed, such as [, \, ], ^, _ and ` for ASCII. You should use isalpha() from <ctype.h>.
If multiple words appear in the line, the letters in the first 10 bytes, as a single blob for all lines. Separate the output with newlines.
You do not check for the end of line, so 10 bytes are tested even by reading beyond the end of line, whose contents are indeterminate.
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
void print_players(void) {
char line[1024];
FILE *file_statistics_reading = fopen("statistics.txt", "r");
if (file_statistics_reading == NULL) {
perror("cannot open statistics.txt");
return;
}
while (fgets(line, sizeof(line), file_statistics_reading) != NULL) {
char player[11];
size_t n = 0;
for (size_t i = 0; n < sizeof(player) - 1 && line[i] != '\0'; i++) {
if (isalpha((unsigned char)line[i]) {
player[n++] = line[i];
}
}
player[n] = '\0';
printf("%s\n", player);
}
fclose(file_statistics_reading);
}
Here is an alternative approach to print the first word from the line:
#include <stdio.h>
#include <string.h>
void print_players(void) {
char line[1024];
FILE *file_statistics_reading = fopen("statistics.txt", "r");
const char *letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (file_statistics_reading == NULL) {
perror("cannot open statistics.txt");
return;
}
while (fgets(line, sizeof(line), file_statistics_reading) != NULL) {
int start = strcspn(line, letters); // skip non letters
int len = strspn(line + start, letters); // count letters in word
printf("%.*s\n", len, line + start);
}
fclose(file_statistics_reading);
}

C program with fully justification of text from file

Here is my problem statement:
I have a small question related to a portion of code which I can't find a proper solution to. Again I'm not necessarily asking for a full solution I just hit a deadend. I need to read from a file lines (Don't know their lengths) find the maximum length of a line and add spaces between words on each line, evenly, so that they are fully justified (all lines have the same size as the max one).
Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *f;
char *word;
int j, i, m, n, c, k, z;
char aux[255] = "", aux1[255];
i = 0;
j = 0;
char file[100][100];
char s[100];
f = fopen("user_input.txt", "r");
m = 0;
while (fgets(file[i], sizeof(file[i]), f)) {
if (m < strlen(file[i]) - 1)
m = strlen(file[i]) - 1;
i++;
}
for (j = 0; j < i; j++) {
n = 0;
for (k = 0; k < strlen(file[j]); k++)
if (file[j][k] == ' ')
n++;
c = (m - strlen(file[j])) / n;
for (z = 0; z < c; z++)
aux[z] = ' ';
for (k = 0; k < strlen(file[j]); k++)
if (file[j][k] == ' ') {
strcpy(aux1, file[j] + k + 1);
strcat(file[j], aux);
strcat(file[j], aux1);
}
printf("%s", file[j]);
}
}
Your code is broken for multiple reasons:
you forgot to include <string.h>
you have hard coded limits to the maximum line length and the number of lines, both causing a penalty of 0.5p
you do not test for fopen() success, causing undefined behavior upon failure to open the file.
you do not test array boundaries when reading lines, causing undefined behavior if the file has more than 100 lines or 99 byte fragments thereof.
your computation of c = (m - strlen(file[j])) / n; is rounded down. You will not insert enough spaces for full text justification in many cases.
aux is not properly null terminated, it will keep growing up to the largest number of spaces to insert for any given line.
the insertion operation will corrupt the line and ultimately leave just the last word with some spaces inserted before.
the code was badly formated and since you do not use {} for non trivial statement, it is difficult to read and easy to break.
Here is a modified version that does not have such limitations:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
const char *filename = "user_input.txt";
FILE *f;
char *line;
int c, i, len, maxlen, skip, nw, spaces, ns;
/* open the file */
if ((f = fopen(filename, "r")) == NULL) {
fprintf(stderr, "cannot open %s\n", filename);
return 1;
}
/* first pass: determine the maximum line length */
for (maxlen = len = 0;;) {
c = getc(f);
if (c == '\n' || c == EOF) {
if (maxlen < len)
maxlen = len;
len = 0;
if (c == EOF)
break;
} else {
len++;
}
}
/* allocate the line buffer: maxlen characters plus newline plus '\0' */
if ((line = malloc(maxlen + 2)) == NULL) {
fprintf(stderr, "cannot allocate memory for %d bytes\n", maxlen + 2);
fclose(f);
return 1;
}
/* second pass: read one line at a time */
rewind(f);
while (fgets(line, maxlen + 2, f)) {
len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
/* strip the newline if any */
line[--len] = '\0';
}
/* skip and output initial spaces */
for (skip = 0; line[skip] == ' '; skip++) {
putchar(line[skip]);
}
/* count the words */
for (nw = 0, i = skip; i < len; i++) {
if (line[i] == ' ')
nw++;
}
/* output the text, expanding spaces */
spaces = maxlen - len;
for (i = skip; i < len; i++) {
if (line[i] == ' ') {
ns = spaces / nw;
printf("%*s", ns, "");
spaces -= ns;
nw--;
}
putchar(line[i]);
}
putchar('\n');
}
free(line);
fclose(f);
return 0;
}

C Store number of a text file in an array

Hi i have this text document
Now i want to store only the numbers in an array how can i do it in c language??
www.google.com *** 64
www.victoria.org **** 118
www.example.org *** 120
This should do it:
#include <stdio.h>
// read in up to 10 lines
int LINES[10];
int main(){
int current = 0;
// open file for reading
FILE* file;
file = fopen("file.txt", "r");
// read whole file one char at a time
while (1){
char ch;
if(!fread(&ch, 1, 1, file)){
break;
}
if (ch >= '0' && ch <= '9'){
int val = ch - '0';
LINES[current] *= 10;
LINES[current] += val;
}else if (ch == '\n'){
current += 1;
}
}
// Looping over the results
for (int i = 0; i <= current; i += 1){
printf("Line %d = %d\n", i, LINES[i]);
}
}
There are multiple ways you can do this.
One way is to read the numbers into a temporary char[] array one character at a time with fgetc, then convert it to an int with the use of atoi().
To test if characters are integers, you can use the isdigit function from <ctype.h>, or you can simply test ch >= '0' && ch <= '9', either way works.
Here is some sample code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define NUMLINES 10
#define NUMLEN 5
int main(void) {
FILE *fp;
int LINES[NUMLINES], i, count = 0, ch, blen = 0;
/* static temp buffer, make it bigger if integers will be more than 5 digits */
/* dynamic memory allocation always possible here */
char buffer[NUMLEN+1];
/* opening the file, with error checking */
fp = fopen("urls.txt", "r");
if (!fp) {
fprintf(stderr, "%s\n", "Error reading file");
return 1;
}
/* read each character until EOF */
while ((ch = fgetc(fp)) != EOF) {
/* digit found. add to temp buffer */
if (isdigit(ch)) {
buffer[blen++] = ch;
}
/* stop adding to buffer, now convert and add to array */
if (ch == '\n') {
buffer[blen] = '\0';
LINES[count++] = atoi(buffer);
blen = 0;
}
}
/* print the array */
for (i = 0; i < count; i++) {
printf("LINES[%d] = %d\n", i, LINES[i]);
}
return 0;
}
Which should output:
LINES[0] = 64
LINES[1] = 118
LINES[2] = 120

Convert a set of chars to int from a file

I'm reading:
22:5412:99:00 (...)
From a text file using (ch=fgetc(fp)) != EOF because I don't have only those numbers to read.
Identifying a number is easy with if(ch >= 48 && ch <= 57) but the thing is I want to put those numbers 22, 5412 into an array of integers. However when I read a char it reads part of number since each number is char.
It gets 2 (and not 22 like I want to) and in the next iteration reads the other 2. How can I save each set of numbers into it's own integer?
I hope I was clear enough, thanks!
My idea is to read in each char, and if it is a digit append it to a buffer. Whenever we get a non-digit, we just read the contents of the buffer as a string using sscanf, and clear the buffer for the next value.
#include <stdio.h>
#include <stdlib.h>
int read_buffer(char* buffer, int* sz)
{
int ret;
if (*sz==0) return 0;
buffer[*sz]='\0'; //end the string
sscanf(buffer,"%d", &ret); //read the contents into an int
*sz=0; // clear the buffer
return ret;
}
int main()
{
char buffer[1000];
int sz=0;
char ch;
FILE* input=fopen("input.txt","r");
// "input.txt" contains 22:5412:99:00
while ((ch=fgetc(input))!=EOF)
{
int number;
if (isdigit(ch))
{
buffer[sz++]=ch; // append to buffer
}
else
{
printf("Got %d\n",read_buffer(buffer,&sz)); // read contents of buffer and clear it
}
}
if (sz) // check if EOF occured while we were reading a number
printf("Got %d\n",read_buffer(buffer,&sz));
fclose(input);
return 0;
}
You would need to store the numbers as a string or a char* and use atoi to actually convert it to a number. http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/
Assuming your pattern is of the type NN:NNNN:NN:NN, parse on the delimiter, feeding characters into a buffer:
int idx = 0, nIdx = 1;
int firstN, secondN, thirdN, fourthN;
char buf[5];
...
while ((ch=fgetc(fp)) != EOF) {
if (ch != ':') {
buf[idx++] = ch;
}
else {
buf[idx] = '\0';
idx = 0;
switch (nIdx++): {
case 1: firstN = atoi(buf); break;
case 2: secondN = atoi(buf); break;
case 3: thirdN = atoi(buf); break;
}
}
}
buf[idx] = '\0';
fourthN = atoi(buf);
...
I did a full program out of the previous post -- and some testing :-)
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
/* fill `array` with at most `siz` values read from the stream `fp` */
/* return the number of elements read */
size_t fillarray(int *array, size_t siz, FILE *fp) {
int ch;
size_t curr = 0;
int advance_index = 0;
while ((ch = fgetc(fp)) != EOF) {
if (isdigit((unsigned char)ch)) {
array[curr] *= 10;
array[curr] += ch - '0';
advance_index = 1;
} else {
if (advance_index) {
advance_index = 0;
curr++;
if (curr == siz) { /* array is full */
break;
}
}
}
}
return curr + advance_index;
}
int main(void) {
int array[1000] = {0};
int n, k;
n = fillarray(array, 1000, stdin);
if (n > 0) {
printf("%d values read:\n", n);
for (k=0; k<n; k++) {
printf(" %d", array[k]);
}
puts("");
} else {
fprintf(stderr, "no data read\n");
}
return 0;
}
And a test run
$ ./a.out
24555:76423 foobar 76235 jgfs(8) jhg x86-64 passw0rd RS232
[CTRL+D]
8 values read:
24555 76423 76235 8 86 64 0 232

Resources