Reading in bytes of a file for printable ASCII characetrs - c

I have to write a program that takes a file name from the command line.
It then read several bytes from the file, looking for strings of printable characters (ASCII values between 32 and 126 decimal).
Then print out the strings.
A string is a run of at least 4 consecutive printable characters and ends whenever a non-printable character is encountered.
Whenever such a string is found, print it out on a new line.
What I have so far is:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
FILE *fp;
fp = fopen(argv[1], "rb");
char buffer;
while(fp != NULL)
{
fread(&buffer, 1, 1, fp);
}
fclose(fp);
}
What I think this does it take the program from the command line and read in all the bytes of the file 1 by 1 and store them into buffer.
Now I need to check each part of the array and see if each element is between 32 and 136.
If it is, I add those bytes to another array until there is a byte not in this range.
Do this for the entirety of the buffer array.
Is this a code approach and is this right so far?

Change the while loop a bit. What you're checking is whether the file exits or not in a loop which won't fetch the required result you want.
fp is comapared with NULL to find out if the file opening is succesful or not as fopen returns address of the file if it opens a file or NULL saying something went wrong.
if( fp == NULL )
{
perror("Error while opening the file\n");
exit(0);
}
What you want do is following lines:
while( ( ch = fgetc(fp) ) != EOF ) { // reads character by character from the file
if((ch <32) || (ch>136)) // check if it is in range to printed
break;
else
printf("%c",ch); // format whoever you want
}

If I understand you correctly, you want your program to read characters from a file (the file might contain non-printable characters), and check if the character falls in the range of 32 to 126 (printable character). If it is, then add that character to a buffer and read more characters until a non-printable character is found. It should also make sure that the string should have at least 4 characters; string should be printed on a newline.
Here is the code that might help you. It was compiled with gcc, and I hope it works for you too.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
FILE *fp;
char buf[100], ch; //the size of the array would vary according to your need
int i=0;
//check for enough arguments
if(argc<2)
{
printf("\nInsufficient Arguments.\n");
printf("\nUsage: PrintChar <file>\n\n");
return 1;
}
//open the file in binary mode and check for exisitence of the file
if((fp = fopen(argv[1], "rb"))== NULL)
{
printf("\nError: Unable to open the file.\n");
return 2;
}
i=0;
while( (ch = fgetc(fp))!=EOF )
{
//check for the range
if(ch>=32 && ch<=126)
{
buf[i] = ch; i++;
//This loop will run till it find a next unprintable character (not between the range of 32 and 126
//we also check for the EOF while reading the characters
while( ( (ch = fgetc(fp))>=32 && ch<=126 ) && ch!=EOF )
{
buf[i] = ch; i++;
}
buf[i] = '\0'; //adding the NULL character
//if the string is at least of 4 letters, print it
if(i>=4)
printf("\n%s", buf);
//reset the counter
i=0;
}
}
fclose(fp);
return 0;
}
File contents - test.txt, that I used:
---------------------------------------------------------------
This is a string
anotherline of text #$%#$%#$% #$% #$%345#$$%&$&##$!##~#######
!∞▬345345µ∞#452353453$%##$%#$%$%%^&%^*4234346443754754451} 
and this is the output of the program:
C:\Users\shine\Documents\MYCPROGS\forStackoverflow>printchar test.txt
This is a string
anotherline of text #$%#$%#$% #$% #$%345#$$%&$&##$!##~#######
345#$%##$%##452353453$%##$%#$%$%%^&%^*4234346443754754451}
345345
-------------------------------------------------------------------
Hope this would be helpful. I made this is a hurry, so please let me know if you find something wrong in it.

Read one character each time, write when we find long enough string or have to:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
size_t min_str_len = 4;
size_t buf_len = 4; /* Must greater than or equal to min_str_len */
char buf[buf_len], ch;
size_t out_len, last_len;
last_len = out_len = 0;
while (fread(&ch, 1, 1, stdin) > 0) {
if (isprint(ch)) {
buf[out_len++] = ch;
if (out_len >= buf_len) {
fwrite(buf, 1, out_len, stdout);
last_len += out_len;
out_len = 0;
}
}
else {
if (out_len + last_len >= min_str_len) {
fwrite(buf, 1, out_len, stdout);
#ifdef NEWLINE
fwrite("\n", 1, 1, stdout);
#endif
last_len = out_len = 0;
}
else {
out_len = 0;
}
}
}
exit(EXIT_SUCCESS);
}
If you want to read more than one byte each time, this "at least 4 consecutive printable characters" will make it a little bit tricky:
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
size_t str_min_len = 4;
size_t buf_len = 1024; /* Must greater than or equal to str_min_len */
char in_buf[buf_len], out_buf[buf_len];
size_t out_len, in_len, last_len;
last_len = out_len = 0;
while ((in_len = fread(in_buf, 1, buf_len, stdin)) > 0) {
assert(out_len == 0);
for (size_t i = 0; i < in_len; i++) {
char ch = in_buf[i];
if (isprint(ch)) {
out_buf[out_len++] = ch;
}
else {
if (out_len + last_len >= str_min_len) {
fwrite(out_buf, 1, out_len, stdout);
#ifdef NEWLINE
/* Write a newline between strings. */
fwrite("\n", 1, 1, stdout);
#endif
last_len = 0;
}
out_len = 0;
}
}
if (0 < out_len && out_len < str_min_len) {
size_t pad_len = str_min_len - out_len;
for (size_t i = 0; i < pad_len; i++) {
char ch;
if (fread(&ch, 1, 1, stdin) < 1) {
exit(EXIT_SUCCESS);
}
else if (isprint(ch)) {
out_buf[out_len++] = ch;
}
else {
break;
}
}
}
if (out_len >= str_min_len) {
fwrite(out_buf, 1, out_len, stdout);
last_len = out_len;
out_len = 0;
}
else {
last_len = out_len = 0;
}
}
exit(EXIT_SUCCESS);
}

Related

pallindrome is not copied to next file but printed on output screen

I have a file named fp1 containing different names, some being palindromes, and have to read all names from fp1 and check if each name is a palindrome or not. If it's a palindrome the I need to print the name to screen and copy it to another file named fp.
Here's my program:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
void main() {
FILE *fp, *fp1;
char m, y[100];
int k = 0, i = 0, t = 1, p = 0;
fp = fopen("C:\\Users\\HP\\Desktop\\New folder\\file 2.txt", "w");
fp1 = fopen("C:\\Users\\HP\\Desktop\\New folder\\file4.txt", "r");
if (fp == NULL) {
printf("error ");
exit(1);
}
if (fp1 == NULL) {
printf("error");
exit(1);
}
k = 0;
m = fgetc(fp1);
while (m != EOF) {
k = 0;
i = 0;
t = 1;
p = 0;
while (m != ' ') {
y[k] = m;
k = k + 1;
m = fgetc(fp1);
}
p = k - 1;
for (i = 0; i <= k - 1; i++) {
if (y[i] != y[p]) t = 0;
p = p - 1;
}
if (t == 1) {
fputs(y, fp);
printf("%s is a pallindrome\n", y);
}
m = fgetc(fp1);
}
fclose(fp);
fclose(fp1);
}
coping pallindrome from one file to next file
You are not null terminating your buffer before attempting to use the contents as a string. After placing the last valid character read by fgetc into the buffer, you must place a null terminating character (\0).
A character buffer without a null terminating byte is not a string. Passing such a buffer to fputs, or the printf specifier %s without a length bound, will invoke Undefined Behaviour.
fgetc returns an int, not a char. On systems where char is unsigned, you will not be able to reliably test against the negative value of EOF.
The inner while loop is not checking for EOF. When the file is exhausted, it will repeatedly assign EOF to the buffer, until the buffer overflows.
To that end, in general, the inner while loop does nothing to prevent a buffer overflow for longer inputs.
In a hosted environment, void main() is never the correct signature for main. Use int main(void) or int main(int argc, char **argv).
Note that fputs does not print a trailing newline. As is, you would fill the output file full of strings with no delineation.
The nested while loops are fairly clumsy, and I would suggest moving your palindrome logic to its own function.
Here is a refactored version of your program. This program discards the tails of overly long words ... but the buffer is reasonably large.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 1024
FILE *open_file_or_die(const char *path, const char *mode)
{
FILE *file = fopen(path, mode);
if (!path) {
perror(path);
exit(EXIT_FAILURE);
}
return file;
}
int is_palindrome(const char *word, size_t len)
{
for (size_t i = 0; i < len / 2; i++)
if (word[i] != word[len - i - 1])
return 0;
return 1;
}
int main(void)
{
/*
FILE *input = open_file_or_die("C:\\Users\\HP\\Desktop\\New folder\\file4.txt", "r");
FILE *output = open_file_or_die("C:\\Users\\HP\\Desktop\\New folder\\file 2.txt", "w");
*/
FILE *input = stdin;
FILE *output = stdout;
char buffer[BUFFER_SIZE];
size_t length = 0;
int ch = 0;
while (EOF != ch) {
ch = fgetc(input);
if (isspace(ch) || EOF == ch) {
buffer[length] = '\0';
if (length && is_palindrome(buffer, length)) {
fputs(buffer, output);
fputc('\n', output);
printf("<%s> is a palindrome.\n", buffer);
}
length = 0;
} else if (length < BUFFER_SIZE - 1)
buffer[length++] = ch;
}
/*
fclose(input);
fclose(output);
*/
}

How do I print specfic parts of a string in a file

I'm writing a program that will read from /etc/passwd and output the username and shell.
For example, here is the first line:
root:x:0:0:root:/root:/bin/bash
I need to only output the user and the shell. In this instance it would print:
root:/bin/bash
The values are seperated by : so I just need to print the string before the first : and the string after the 6th :. How can I do that?
Here is the code I have so far:
#include <unistd.h>
#include <fcntl.h>
//printf and scanf prototype
int printf(const char *text, ...);
int scanf(const char *format, ...);
int main(void)
{
int fd;
int buff_size = 1;
char buff[512];
int size;
fd = open("/etc/passwd",O_RDONLY);
if (fd < 0)
{
printf("Error opening file \n");
return -1;
}
while ((size = read(fd,buff,1))>0)
{
buff[1] = '\0';
printf("%s",buff);
}
}
So far the code reads from /etc/passwd and prints out the whole file line by line. I need to configure the printf statement to just print the user and shell for each instance.
I just need to print the string before the first : and the string after the 6th :.
Filter the string, put the needed data into an output buffer, and print the output buffer.
Since you already used char buff[512]; as the original data buffer, then just create another buffer for output. i.e. char outbuff[512];
// char buff[512]; assume already defined as that in PO
// char outbuff[512]; assume already defined as that in PO
void customized_print(char* buff, char* outbuff) {
int i = 0, j = 0, cnt = 0;
while (i < 512) { // copy until first ':'
if((outbuff[j++]=buff[i++]) == ':') break;
}
// skip 5 more ':'
while (i < 512 && cnt < 5) {
if(buff[i++] == ':') cnt++;
}
// copy the remains
while (i < 512) {
if( (outbuff[j++]=buff[i++]) == '\0' ) break;
}
printf("%s\n", outbuff);
outbuff[0] = 0; // clean the outbuff
}
Another solution with a demo of full code.
If you do not need to use buff. You can use the following solution.
#include <stdio.h>
void customized_print(char* buff, char* outbuff) {
int cnt = 0;
char* p = outbuff;
while (*buff) { // copy until first ':'
if((*p++=*buff++) == ':') break;
}
// skip 5 more ':'
while (*buff && cnt < 5) {
if(*buff++ == ':') cnt++;
}
// copy the remains
while (*buff) {
if( (*p++=*buff++) == '\0' ) break;
}
printf("%s\n", outbuff);
outbuff[0]=0; // clean the outbuff
}
int main(){
char outbuff[512];
customized_print("root:x:0:0:root:/root:/bin/bash", outbuff);
customized_print("foo:x:0:0:root:/root:/bin/bash", outbuff);
return 0;
}
Compile and run:
gcc -Wall demo.c -o demo.out
./demo.out
Output:
root:/bin/bash
foo:/bin/bashh

Reading, a set line range from a file in C

I' am writing a C program which allows the user to dynamically specify the File name from which the data is to be read. Next the user enters a lower bound and an upper bound. The data in the lines from between the bounds is to be printed.
For this the main function makes a call: readValues(cTargetName, iLower, iHiger);
The function readValues is supposed to work as follows:
Check if file exist, if yes. Open it with fopen
Read with feof and fgets line by line the whole file, and store each line in char string
With a for loop, print the correct range of lines from the string
I'm not sure why but the while loop doesn't seem to exit although I use the feof statement, which should terminate after the end of the File is reached.
The code looks as follows:
#include <stdio.h>
#include <stdlib.h>
void readValues(char cFileName[75], int n, int m)
{
//Variable declaration;
char strArray[50][50];
char *parser;
int i = 0;
FILE *Data;
if(Data = fopen(cFileName, "rt") == NULL){
printf("File could not be opened");
return 1; //Can you return 1 in a void function?
}
//Read the file line by line
while(feof(Data)==0){
fgets(strArray[i], 200, Data);
i++;
}
//Reading the specified lines
for(n; n<=m; n++){
printf("%s", strArray[n]);
}
}
int main()
{
char cTargetName[75] = {"C:/Users/User1/Desktop/C_Projects_1/TestData.txt"};
int iLower = 2;
int iHiger = 4;
readValues(cTargetName, iLower, iHiger);
return 0;
}
All help is appreciated. Thanks in advance!
Here is my solution to your question:
#include <stdio.h>
#include <stdlib.h>
#define MIN_LINE_LENGTH 64
typedef enum {
false, true
} bool;
int main() {
char filename[PATH_MAX] = {0};
printf("Enter filename:\n");
fgets(filename, PATH_MAX, stdin); // get filename from stdin
char *ptr = filename;
while (*ptr) { // remove trailing newline at the end of filename (fgets() includes newline)
if (*ptr == '\n') {
*ptr = 0;
}
++ptr;
}
printf("Enter starting line and end line, separated by a space:\n");
size_t startLine = 0;
size_t endLine = 0;
bool hasFirstNum = false;
bool hasSecondNum = false;
bool hasMiddleSpace = false;
bool hasLastSpace = false;
size_t numCount = 0;
int ch;
while ((ch = fgetc(stdin)) != EOF && ch != '\n') { // continually receive chars from stdin
if (ch != 32 && !(ch >= 48 && ch <= 57)) { // if not a space or number, raise error
fprintf(stderr, "Only numerical values (and spaces) can be entered.\n");
return 1;
}
if (ch == 32) {
if (hasFirstNum) {
hasMiddleSpace = true;
}
if (hasSecondNum) {
hasLastSpace = true;
}
continue;
}
else if (!hasFirstNum) {
++numCount;
hasFirstNum = true;
}
else if (!hasSecondNum && hasMiddleSpace) {
++numCount;
hasSecondNum = true;
}
else if (hasLastSpace) {
++numCount;
}
if (numCount == 1) {
startLine *= 10;
startLine += ch - 48; // '0' character in ASCII is 48
}
else if (numCount == 2){
endLine *= 10;
endLine += ch - 48;
}
else {
break;
}
}
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Error opening file.\n");
return 1;
}
char **lines = malloc(sizeof(char *));
char *line = malloc(MIN_LINE_LENGTH);
*lines = line;
int c;
size_t char_count = 0;
size_t line_count = 1;
while ((c = fgetc(fp)) != EOF) { // continually get chars from file stream
if (c == '\n') { // expand lines pointer if a newline is encountered
*(line + char_count) = 0;
++line_count;
lines = realloc(lines, line_count*sizeof(char *));
line = (*(lines + line_count - 1) = malloc(MIN_LINE_LENGTH));
char_count = 0;
continue;
}
if ((char_count + 1) % MIN_LINE_LENGTH == 0 && char_count != 0) { // expand line pointer if needed
line = realloc(line, char_count + MIN_LINE_LENGTH);
}
*(line + char_count) = c;
++char_count;
}
*(line + char_count) = 0; // to ensure the last line always ends with the null byte
if (startLine >= line_count) { // raise error if starting line specified is greater than num. of lines in doc.
fprintf(stderr, "Specified starting line is less than total lines in document.\n");
return 1;
}
if (endLine > line_count) { // adjust ending line if it is greater than number of lines in doc.
endLine = line_count;
}
if (startLine == 0) { // we will be using the starting index of 1 as the first line
startLine = 1;
}
char **linesPtr = lines + startLine - 1;
while (startLine++ <= endLine) { // print lines
printf("%s\n", *linesPtr++);
}
for (size_t i = 0; i < line_count; ++i) { // free all memory
free(*(lines + i));
}
free(lines);
return 0;
}
It is a little more convoluted, but because it uses dynamic memory allocation, it can handle lines of any length within a text file.
If there is anything unclear, please let me know and I would be happy to explain.
Hope this helps!!
several issues here,
first, you limited the length of lines to 200, not exactly what you might expect to get.
the fgets function returns lines up to specified length unless hit by newline character - this should be taken into account.
additionally, fgets returns NULL if you hit EOF - no real need to use feof.
second, you could save yourself a lot of pain and simply count the number of times you get a string, and for the times you are within the range just print it immediately. will save you a nice amount of overhead
like this:
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 200//or anything else you want
void readValues(char cFileName[75], int n, int m)
{
//Variable declaration;
char line[MAXLINE];
int i = 0;
FILE *Data;
if((Data = fopen(cFileName, "rt")) == NULL){
printf("File could not be opened");
return 1; //Can you return 1 in a void function?
}
//Read the file line by line and print within range of lines
while((line=fgets(line, MAXLINE,Data))!=NULL){//terminates upon EOF
if (++i>=n&&i<=m)
printf(""%s\n",line);
}
}

Can't get output reversed reading from a file C

I want to do something pretty simple, but somehow it doesn't do what I want.
I have a file with numbers in it. I wanna read from that file and print those numbers but in reverse order.
so let's say we have the numers:
10
32
43
6
7
I want to have:
7
6
43
32
10
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
int main(int argc, char *argv[]) {
char userInput[MAX], target[MAX];
FILE *file = stdin;
if (argc > 2) {
fprintf(stderr, "Usage: %s[<file>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2) {
file = fopen(argv[1], "r");
if (!file) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
while(fgets(userInput, sizeof(userInput), file)) {
size_t len = strlen(userInput);
int i;
for (i = len-1; i >= 0; i--) {
if (userInput[i] == ' ') {
userInput[i] = '\0';
printf("%s ", &(userInput[i]) + 1);
}
}
printf("%s", userInput);
}
if (file != stdin) {
fclose(file);
}
exit(EXIT_SUCCESS);
}
Does anyone see the mistake?
This is the content of my .txt file:
11
34
45
3
78
43
3
4
9
34
23
43
Your program is designed to accept all of its input on a single line. Your input file contains multiple lines.
The following 1. solution is based on the example shown in the question.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
int main(int argc, char *argv[])
{
char userInput[MAX] = "";
char target[MAX] = "";
FILE *file = stdin;
if (argc > 2)
{
fprintf(stderr, "Usage: %s[<file>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2)
{
file = fopen(argv[1], "r");
if (!file)
{
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
/* Read all numbers from file and write it to target buffer */
while(fgets(userInput, sizeof(userInput), file))
{
/* Break loop if there is not enough space to store current number. */
if ((strlen(target) + strlen(userInput)) >= MAX)
{
break;
}
/* Add current number read from file to target buffer */
strcat(target, userInput);
/* Print current number read from file */
printf("%s", userInput);
}
if (file != stdin) {
fclose(file);
}
/*
* Run over target buffer in reverse order and replace current number
* split character '\n' by string end marker '\0'.
* After that print current number.
*/
size_t len = strlen(target);
if (len > 0)
{
for (size_t i = len-1; i != 0; i--)
{
if (target[i] == '\n')
{
target[i] = '\0';
printf("%s\n", &(target[i]) + 1);
}
}
/* Print first number */
printf("%s\n", &(target[0]));
}
exit(EXIT_SUCCESS);
}
However it might be better to store the numbers within an array of integer using sscanf() and after that print the content of the array in reverse order.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
int main(int argc, char *argv[])
{
char userInput[MAX] = "";
int nums[MAX];
int count = 0;
FILE *file = stdin;
if (argc > 2)
{
fprintf(stderr, "Usage: %s[<file>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2)
{
file = fopen(argv[1], "r");
if (!file)
{
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
/* Read all numbers from file and write it to target buffer */
while(fgets(userInput, sizeof(userInput), file))
{
sscanf(userInput, "%i", &nums[count]);
/* Break loop if there is not enough space to store current number. */
if (count >= MAX)
{
break;
}
count++;
/* Print current number read from file */
printf("%s", userInput);
}
if (file != stdin) {
fclose(file);
}
/* Print numbers stored in array in reverse order */
printf("\n");
for (int idx = count; idx != 0; idx--)
{
printf("%i\n", nums[idx-1]);
}
exit(EXIT_SUCCESS);
}
You can read your input file starting from the end using fseek
Here there's a possible implementation based on your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
int main(int argc, char *argv[]) {
int i = 0, end = 0, cnt = 0;
char ch, cnt_flag = 0;
char userInput[MAX];
FILE *fp = stdin;
if (argc > 2) {
fprintf(stderr, "Usage: %s[<fp>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2) {
fp = fopen(argv[1], "r");
if (!fp) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
fseek(fp, 0, SEEK_END);
end = ftell(fp);
while (i < end) {
i++;
fseek(fp, -i, SEEK_END);
ch = fgetc(fp);
if (cnt_flag == 1) {
cnt++;
if (ch == '\n') {
/* skip empty lines */
if (cnt > 1) {
fgets(userInput, cnt, fp);
printf("%s\n", userInput);
}
cnt_flag = 0;
cnt = 0;
}
}
if (ch == '\n')
cnt_flag = 1;
}
/* read first line */
fseek(fp, 0, SEEK_SET);
fgets(userInput, cnt + 1, fp);
printf("%s\n", userInput);
if (fp != stdin) {
fclose(fp);
}
exit(EXIT_SUCCESS);
}
If u need to reverse strings in file, check this out. This is crossplatform solution without big chunk buffer usage. Only restriction that this is for file operations only. Advantages: file size independent solution.
This example based on your code.
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#define NEW_LINE 2 // Windows new line: 2 bytes (CR) + (LF)
#else
#define NEW_LINE 1 // Linux new line: 1 byte (LF)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
// This function can be replaced with any other solution
// u need (write data into another file, or allocate data in the memory and etc)
void printLineFromFile(FILE *file) {
char buff[255];
// write line with \n to buff
fgets(buff, 255, file);
// total buff size
int buffLen = strlen(buff);
int printLen = buffLen;
// printLen equals buffLen if the last symbol isn't \n
if (buff[buffLen - 1] == '\n') printLen--;
// print specified amount of bytes from buff
fprintf(stdout, "%.*s\n", printLen, buff);
}
int main(int argc, char *argv[]) {
FILE *file = stdin;
if (argc > 2) {
fprintf(stderr, "Usage: %s[<file>]\n", argv[0]);
exit(EXIT_FAILURE);
}
if (argc == 2) {
file = fopen(argv[1], "r");
if (!file) {
perror(argv[0]);
exit(EXIT_FAILURE);
}
}
// set position to the end of file
fseek(file, 0, SEEK_END);
// endPosition - position of last byte in file. (not EOF)
long endPosition = ftell(file) - 1;
// currPosition - moving position across the file
long currPosition = endPosition;
// byte buffer to read into it
int ch;
while (currPosition >= 0) {
// moving file position to the currPosition
fseek(file, currPosition, SEEK_SET);
if (currPosition == 0) {
printLineFromFile(file);
currPosition -= NEW_LINE;
continue;
}
ch = fgetc(file);
if (ch == '\n') {
if (currPosition == endPosition) {
currPosition -= NEW_LINE;
fprintf(stdout, "\n");
}
else {
printLineFromFile(file);
currPosition -= NEW_LINE;
continue;
}
}
// move back to position before fgetc
currPosition--;
};
if (file != stdin) {
fclose(file);
}
exit(EXIT_SUCCESS);
}

How to indicate the last line of the file

I want to read the txt line by line and store it to the array;
I am like success to store line by line in array;
I use printf("%s", loadtext[i]) with i= 0,1,2,3-20 respectively to check that things in stored in array;
but I realize that the for loop had done 1912 times when I
type printf("%d", i); right behind the for loops,
Suppose my txt is store like this:
I am a jerk
I am a noob
I am an idiot
I am done
I have another program to add new lines to the text file while this program is running.
How can I detect I am done or the new line added later is the last line to not allow the for loop do so many times?
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
int main(){
FILE *fp = fopen("abc.txt","r");
int i = 0,j=0,k=0;
char ch[10000];
char loadtext[100][100];
for (i=0; ch[i] != EOF; i++){
ch[i] = fgetc(fp);
if (ch[i] != '\n'){
loadtext[j][k] = ch[i];
k++;
}
if(ch[i] == '\n'){
loadtext[j][k] = ch[i];
k=0;
j++;
}
}
printf("%s", loadtext[0]);
printf("%s", loadtext[1]);
printf("%s", loadtext[2]);
fclose(fp);
return 0;
}
To read a whole file into an "array" of pointers to char which represent the lines, you can do:
#include <stddef.h> // size_t
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
#include <stdio.h> // malloc(), realloc(), free(), fgets()
#include <string.h> // strlen(), strcpy()
enum { BUFFER_SIZE = 30 }; // whatever line length you suspect the input file to be + 1
int main(void)
{
char const *filename = "test.txt";
FILE *is = fopen(filename, "r");
if (!is) {
fprintf(stderr, "Couldn't open \"%s\" for reading :(\n\n", filename);
return EXIT_FAILURE;
}
int result = EXIT_SUCCESS; // assume a happy ending
char buffer[BUFFER_SIZE];
size_t num_lines = 0;
char **lines = NULL;
while (fgets(buffer, sizeof(buffer), is)) {
++num_lines;
char **temp = realloc(lines, num_lines * sizeof(*lines));
if (!temp) {
fputs("Not enough memory :(\n\n", stderr);
fclose(is);
result = EXIT_FAILURE;
goto cleanup;
}
lines = temp;
size_t length = strlen(buffer);
length = strlen(buffer);
// remove a trailing newline if any:
if (length && buffer[length - 1] == '\n')
buffer[--length] = '\0';
size_t line_length = length;
lines[num_lines - 1] = malloc((length + 1) * sizeof(*lines));
if (!lines[num_lines - 1]) {
fputs("Not enough memory :(\n\n", stderr);
fclose(is);
result = EXIT_FAILURE;
goto cleanup;
}
strcpy(lines[num_lines - 1], buffer);
// as long as the buffer has been filled completely by the previous
// call to fgets() and a next call to fgets() also succeeds:
while (length == BUFFER_SIZE - 1 && fgets(buffer, sizeof(buffer), is)) {
length = strlen(buffer);
// remove a trailing newline if any:
if (length && buffer[length - 1] == '\n')
buffer[--length] = '\0';
char *temp = realloc(lines[num_lines - 1], line_length + length + 1);
if (!temp) {
fputs("Not enough memory :(\n\n", stderr);
fclose(is);
result = EXIT_FAILURE;
goto cleanup;
}
lines[num_lines - 1] = temp;
strcpy(lines[num_lines - 1] + line_length, buffer);
line_length += length;
}
}
fclose(is);
// use lines:
for (size_t i = 0; i < num_lines; ++i)
puts(lines[i]);
cleanup:
for (size_t i = 0; i < num_lines; ++i)
free(lines[i]);
free(lines);
return result;
}
Using only a fixed-size two-dimensional array and fgetc()*):
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
enum { MAX_LINES = 100, MAX_LINE_LENGTH = 100 };
int main(void)
{
char const *filename = "test.txt";
FILE *is = fopen(filename, "r");
if (!is) {
fprintf(stderr, "Couldn't open \"%s\" for reading :(\n\n", filename);
return EXIT_FAILURE;
}
char text[MAX_LINES][MAX_LINE_LENGTH + 1] = { 0 }; // zero-initialize the array
// so we don't have to care
size_t num_lines = 0; // about zero terminating
size_t current_column = 0; // every line
int ch;
// as long as we are still inside the bounds of the fixed size array
// and fgetc() doesn't return EOF
while (num_lines < MAX_LINES && current_column < MAX_LINE_LENGTH &&
(ch = fgetc(is)) != EOF)
{
if (ch == '\n') { // "move" num_lines and current_column to the next
++num_lines; // line.
current_column = 0;
continue;
}
text[num_lines][current_column++] = ch;
}
if (ch != EOF) {
fputs("The file is too big :(\n\n", stderr);
return EXIT_FAILURE;
}
for (size_t i = 0; i <= num_lines; ++i)
puts(text[i]);
}
*) Could be done with fgets() too.
to read the txt line by line and store it to the array
Code has various problems:
ch[i] != EOF accesses ch[i] before it is assigned! Undefined Behavior (UB).
char can be signed or unsigned. EOF is some negative. When char is unsigned the below is an infinite loop. When char is signed, ch[i] != EOF could exit the loop early.
printf("%s", loadtext[0]); attempts to print loadtext[0] as if it was a string. But lacking a certain null character, and thus not a string, this leading to more UB.
Lack of buffer index checks: loadtext[j][k] = ch[i]; k++; may increment k to beyond 100. Similar weak code with i.
Code may fail to open the file and no check provided.
Instead save the result from fgetc() as an int, test for for EOF, test for '\n' and append a null character to form strings.
Some ideas for alternate code:
#define LINES_N 100
#define LINE_SIZE 100
int main(void) {
FILE *fp = fopen("abc.txt", "r");
if (fp) {
// char ch[10000];
char loadtext[LINES_N][LINE_SIZE];
int ch_index = 0;
int line_count = 0;
int character;
int previous_character = '\n';
while ((character = fgetc(fp)) != EOF) {
// Starting a new line?
if (previous_character == '\n') {
if (++line_count > LINES_N) {
printf("Too many lines\n");
return EXIT_FAILURE;
}
}
loadtext[line_count - 1][ch_index++] = (char) character;
loadtext[line_count - 1][ch_index] = '\0';
if (ch_index + 1 >= LINE_SIZE) {
printf("Too long a line\n");
return EXIT_FAILURE;
}
previous_character = character;
}
if (line_count >= 1) printf("%s", loadtext[0]);
if (line_count >= 2) printf("%s", loadtext[1]);
if (line_count >= 3) printf("%s", loadtext[2]);
fclose(fp);
}
return 0;
}

Resources