Using fgets to read and print multiple lines from .txt file - c

FILE *fp = fopen("story.txt", "r");
if(fp == NULL){
printf("\nError opening file.\nExiting program.\n");
exit(1);
}
char text[100];
while(fgets(text, 100, fp) != NULL){
printf("%s", text);
}
printf("\n");
fclose(fp);
I'm trying to print the first 100 characters of a text file, including new lines, however when I use the code above it presents some weird behavior. First of all, it only prints the very last line of the text file, which itself is under 100 characters. Also, if I include two print statements in the while loop i.e.
while(fgets(text, 100, fp) != NULL){
printf("%s", text);
printf("%s", text);
}
It prints a lot more than 125 chars of the text file (somewhere in the thousands, it's a big text file), and the contents of said text is a bunch of seemingly random segments from the file in one constant stream, no new lines or anything.
So I guess my question is is there any way to use fgets so that it prints the text in the file, starting from the top, and includes new lines? I eventually have to use this to turn a text file into a character array, so that I can make a new, modified character array based off of that array, which will be printed to a new text file. So if there is a better way to approach that end goal, that would be appreciated.
EDIT: after some discussion in the comments I've realized that the text I am using is just one big block of text with carriage returns and no newlines. I guess at this point my main problem is how to turn this text file with carriage returns into a character array.

If the goal is to read a text file in lines of 100 characters, and to do away with the carriage returns, you can still use fgets() as long as you remember that fgets() will take characters up to and including the next newline, or until one less than the specified number of characters has been read.
The code below reads a line of text, up to BUFFER_SZ-1 characters, increases the memory allocation to hold a new line of text, and copies the line into the allocated space, removing carriage returns and any trailing newlines. Note that the address of the reallocated space is first stored in a temporary pointer. realloc() returns a null pointer in the event of an allocation error, and this step avoids a potential memory leak.
Since the lines are broken at BUFFER_SZ-1 characters, words may be split across lines, and you may want to develop additional logic to handle this. It would be more efficient to reallocate in larger chunks and less frequently, rather than once for every line, as is done here. Also note that it may be useful to open the file in binary mode to more closely parse line endings.
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SZ 100
int main(void)
{
FILE *fp = fopen("story.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Unable to open file\n");
exit(EXIT_FAILURE);
}
char buffer[BUFFER_SZ];
char (*text)[sizeof buffer] = NULL;
char (*temp)[sizeof buffer] = NULL;
size_t numlines = 0;
while (fgets(buffer, sizeof buffer, fp) != NULL) {
++numlines;
/* Allocate space for next line */
temp = realloc(text, sizeof(*text) * numlines);
if (temp == NULL) {
fprintf(stderr, "Error in realloc()\n");
exit(EXIT_FAILURE);
}
text = temp;
/* Copy buffer to text, removing carriage returns and newlines */
char *c = buffer;
char *line = text[numlines-1];
while (*c != '\n' && *c != '\0') {
if (*c != '\r') {
*line++ = *c;
}
++c;
}
*c = '\0';
}
if (fclose(fp) != 0) {
fprintf(stderr, "Unable to close file\n");
}
for (size_t i = 0; i < numlines; i++) {
printf("%s\n", text[i]);
}
free(text);
return 0;
}
Another option would be to replace the carriage returns with newlines. This may be what OP had in mind. The above program is easily modified to accomplish this. Note that the \n is removed from the printf() statement that displays the results, since newlines are now included in the strings.
...
/* Copy buffer to text, converting carriage returns to newlines */
char *c = buffer;
char *line = text[numlines-1];
while (*c != '\n' && *c != '\0') {
if (*c == '\r') {
*line++ = '\n';
} else {
*line++ = *c;
}
++c;
}
*c = '\0';
}
if (fclose(fp) != 0) {
fprintf(stderr, "Unable to close file\n");
}
for (size_t i = 0; i < numlines; i++) {
printf("%s", text[i]);
}
...

It doesn't copy one line onto the end of another. It simply reuses the buffer you keep passing it. If you want multiple lines stored, copy them to another buffer, and concatenate them. (See: strcat)

Related

Read null characters from file into string

I have created a function where I can read the first x amount of bytes from a file. The file is a binary file, and contains NULL characters. I am trying to read the file into a char* however it gives an incorrect length because I am appending NULL characters to the string. Is there a workaround to storing data from a file in a string
// readlen is the amount I want to read from the start of a file
FILE* fptr = fopen(path, "rb");
char* contents = (char*)malloc(readlen + 1);
int read = 0;
int ch;
while ((ch = fgetc(fptr)) != EOF && read != readlen) {
contents[read++] = (char)ch;
}
contents[readlen] = '\0';
fclose(fptr);
return contents;
First, a terminology misconception in the title "Read null characters from file into string". In C, string can not contain NUL characters, because those mark end of the string. A char buffer can contain them, but it's not a string. And when it is not a string with an end marker, you need to keep track of its length.
Then, to solve your problem, you could return a struct with the information
struct buffer {
char *contents;
size_t size;
};
With that, the code you show becomes something like this:
// readlen is the amount I want to read from the start of a file
FILE* fptr = fopen(path, "rb");
struct buffer buffer;
buffer.contents = (char*)malloc(readlen + 1);
buffer.size = 0;
int ch;
while ((ch = fgetc(fptr)) != EOF && buffer.size != readlen) {
buffer.contents[buffer.size++] = (char)ch;
}
buffer.contents[buffer.size] = '\0';
fclose(fptr);
return buffer;
Further improvement of your function would be to use fread to do the read with one function call. You might discover, that your own function becomes unnecessary and you could just directly call fread, even.
Also, putting extra 0 at the end of the char buffer might not be useful, since it is not a string, but it doesn't really hurt and will be convenient if you ever read a text file and want to print it or something. Still, unless you have some specific need for it, I'd consider removing that extra byte.

Inserting text into a txt file

I'm trying to accomplish the following:
I have a text file with the last printable character "]" in a separate line.
This line is not necessary to be the last line of the file. Some blank lines (line returns) can be there.
The purpose of the project is to insert new text before the line with "]".
The way I try to implement this is to search the file from the end of the file to find the line number with the character "]" (char_line).
Copy line by line from the original file rules1.txt to rules2.txt up to the line char_line. Next step is to append the new text with "]" at the end.
After that, I can delete the original file and rename the new file from rules2.txt to rules1.txt.
The problem I have is that the program finds the line with the character "]" and I can do a printf and see the correct line number.
I am assigning char_line = "%d".
When I'm using if(i < char_line) the file is copied all the way to EOF.
If I assign a numerical value, char_line = 23, the file is copied up to line 22, which is what I want.
This is the part of the code which should find line number for "]", copy line by line rules1.txt to rules2.txt up the line with "]".
#include <stdio.h>
int main(void) {
int end, loop, line;
char str[64];
FILE *file;
FILE *write;
int char_line;
int ret;
file = fopen("rules1.txt", "r");
if (file == NULL) {
printf("Failed to open file\n");
return -1;
}
int ch, line_num = 0;
do {
ch = fgetc(file);
if(ch == '\n')
line_num++;
} while (ch != EOF);
// last line doesn't end with a new line!
// but there has to be a line at least before the last line
if(ch != '\n' && line_num != 0)
line_num++;
fclose(file);
line = line_num-1;
start:
file = fopen("rules1.txt", "r");
if (file == NULL) {
printf("Failed to open file\n");
return -1;
}
for(end = loop = 0;loop<line;++loop){
if(0==fgets(str, sizeof(str), file)){//include '\n'
end = 1;//can't input (EOF)
break;
}
}
if(!end)
if (strncmp ("]", str, 1) == 0){
char_line ="%d";
goto next;
} else if(line >1){
line == line--;//WTF?
fclose(file);
goto start;
} else //What to do here?
next:
file = fopen("rules1.txt", "r");
write = fopen("rules2.txt", "w");
char linec [64]; /* line size */
int i = 0;
while (fgets(linec, sizeof(linec), file)) { /* read line from file */
i++;
if(i < char_line) {
fprintf (write , linec); /* write line to file */
}
}
fclose(file);
fclose(write);
return(0);
}
You have declared int char_line; and later you code
char_line ="%d";
This is nonsense. Since a literal string is some char[] array (better think of it as some constant array), decayed to a pointer, and assigning a pointer to an int does not make sense at all. On many machines (x86-64 notably), a pointer has 64 bits but an int has only 32 bits.
Please, compile your code with all warnings and debug info, so gcc -Wall -Wextra -g with GCC, improve your code to get no warnings, then use the debugger gdb.
Take several days to read some good books on C programming. Be aware and work hard to avoid undefined behavior. Use the debugger to run your program step by step and query its state to understand what is happening.
Read the documentation of every standard function you are using such as fgets.
You probably want a loop that reads every line, and copies sometimes that line to another file.

How to read all the lines in a file and store them in a string

I am trying to read some commands which should be passed to my program from a file. The commands are on different lines, so I guess this means that they are separated by \n character. This is my command reading section:
FILE *fop;
char command[50];
fopen("mbr.op", "r");
while(!feof(fop))
{
fscanf(fop,"%s[^\n]", command);
printf("%s\n", command);
}
fclose(fop);
This prints some words that are in the file, but not all, and not in the expected order. How could I change this code to achieve the desired result?
You open your file incorrectly (it returns a FILE pointer which is associated with the opened file), fopen should be used as this -
fop=fopen("mbr.op", "r");
And while(!feof(fop)) should not be used .
You can write your loop as follows -
while(fscanf(fop,"%[^\n]%*c", command)==1)
{
printf("%s\n", command);
}
Note - Also check if file was opened successfully.
FILE *fop;
char command[50];
fop = fopen("mbr.op", "r"); /* get the file pointer */
if (fop == NULL) exit(1); /* check if the file successfully opened */
while(fscanf(fop,"%49[^\n]%*c", command) == 1) /* see notes after this code */
{
printf("%s\n", command);
}
fclose(fop);
Notes on the usage of fscanf():
They say using feof() is not good.
Yon won't need the s.
Specify the maximum length to read to avoid buffer overflow.
Added %*c to have it skip the newline character.
This code above won't work well if there are blank lines.
I think using fgets() is better to read lines.
FILE *fop;
char command[51]; /* added 1 byte to store the newline character */
fop = fopen("mbr.op", "r");
if (fop == NULL) exit(1);
while(fgets(command, sizeof(command), fop))
{
/* remove the newline character */
char* p;
for(p = command; *p != '\0' && *p != '\n'; p++);
*p = '\0';
/* print what is read */
printf("%s\n", command);
}
fclose(fop);

reading text file, copying into array in C

The code is supposed to read a user-inputted text file name, copy every character into a multidimensional array, then display it with standard output. It compiles, but produces unintelligible text. Am I missing something?
for (i = 0; i < BIGGEST; i++) {
for (j = 0; j < BIGGESTL; j++) {
if (fgetc(array, fp) ) != EOF)
array[i][j] = c;
else array[i][j] = '\0'
}
fclose(fp);
return 0;
}
You stop filling the array when you encounter EOF, but you print the full array out no matter what.
If the data read from the file is smaller than the input array, you will read that data in and then print that data out, plus whatever random characters were in the memory locations that you do not overwrite with data from the file.
Since the requirement seems to be to print text data, you could insert a special marker in the array (e.g. '\0') to indicate the position where you encountered EOF, and stop displaying data when you reach that marker.
You had better read each line from file
For example:
int i = 0;
while(fgets(text[i],1000,fp))
{
i++;
}
Though the question is edited and only part of the code is left in question. I am posting more than what is required for the question at the moment.
Reason being, there can be numberous improvements to originally posted full code.
In main() function:
You need to check for the argc value to be equal to 2 for your purpose and only then read in value of argv[1] . Else if program executed without the command-line-argument which is file_name in this case, invalid memory read occurs, resulting in segmentation fault if you read in argv[1].
In read_file_and_show_the contents() function:
Stop reading file if end of file is reached or maximum characters is read and store in the character array.
Below Program will help you visualize:
#include <stdio.h>
/*Max number of characters to be read/write from file*/
#define MAX_CHAR_FOR_FILE_OPERATION 1000000
int read_and_show_the_file(char *filename)
{
FILE *fp;
char text[MAX_CHAR_FOR_FILE_OPERATION];
int i;
fp = fopen(filename, "r");
if(fp == NULL)
{
printf("File Pointer is invalid\n");
return -1;
}
//Ensure array write starts from beginning
i = 0;
//Read over file contents until either EOF is reached or maximum characters is read and store in character array
while( (fgets(&text[i++],sizeof(char)+1,fp) != NULL) && (i<MAX_CHAR_FOR_FILE_OPERATION) ) ;
//Ensure array read starts from beginning
i = 0;
while((text[i] != '\0') && (i<MAX_CHAR_FOR_FILE_OPERATION) )
{
printf("%c",text[i++]);
}
fclose(fp);
return 0;
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Execute the program along with file name to be read and printed. \n\
\rFormat : \"%s <file-name>\"\n",argv[0]);
return -1;
}
char *filename = argv[1];
if( (read_and_show_the_file(filename)) == 0)
{
printf("File Read and Print to stdout is successful\n");
}
return 0;
}

Reading text file into an array of lines in C

Using C I would like to read in the contents of a text file in such a way as to have when all is said and done an array of strings with the nth string representing the nth line of the text file. The lines of the file can be arbitrarily long.
What's an elegant way of accomplishing this? I know of some neat tricks to read a text file directly into a single appropriately sized buffer, but breaking it down into lines makes it trickier (at least as far as I can tell).
Thanks very much!
Breaking it down into lines means parsing the text and replacing all the EOL (by EOL I mean \n and \r) characters with 0.
In this way you can actually reuse your buffer and store just the beginning of each line into a separate char * array (all by doing only 2 passes).
In this way you could do one read for the whole file size+2 parses which probably would improve performance.
It's possible to read the number of lines in the file (loop fgets), then create a 2-dimensional array with the first dimension being the number of lines+1. Then, just re-read the file into the array.
You'll need to define the length of the elements, though. Or, do a count for the longest line size.
Example code:
inFile = fopen(FILENAME, "r");
lineCount = 0;
while(inputError != EOF) {
inputError = fscanf(inFile, "%s\n", word);
lineCount++;
}
fclose(inFile);
// Above iterates lineCount++ after the EOF to allow for an array
// that matches the line numbers
char names[lineCount][MAX_LINE];
fopen(FILENAME, "r");
for(i = 1; i < lineCount; i++)
fscanf(inFile, "%s", names[i]);
fclose(inFile);
For C (as opposed to C++), you'd probably wind up using fgets(). However, you might run into issues due to your arbitrary length lines.
Perhaps a Linked List would be the best way to do this?
The compiler won't like having an array with no idea how big to make it. With a Linked List you can have a really large text file, and not worry about allocating enough memory to the array.
Unfortunately, I haven't learned how to do linked lists, but maybe somebody else could help you.
If you have a good way to read the whole file into memory, you are almost there. After you've done that you could scan the file twice. Once to count the lines, and once to set the line pointers and replace '\n' and (and maybe '\r' if the file is read in Windows binary mode) with '\0'. In between scans allocate an array of pointers, now that you know how many you need.
you can use this way
#include <stdlib.h> /* exit, malloc, realloc, free */
#include <stdio.h> /* fopen, fgetc, fputs, fwrite */
struct line_reader {
/* All members are private. */
FILE *f;
char *buf;
size_t siz;
};
/*
* Initializes a line reader _lr_ for the stream _f_.
*/
void
lr_init(struct line_reader *lr, FILE *f)
{
lr->f = f;
lr->buf = NULL;
lr->siz = 0;
}
/*
* Reads the next line. If successful, returns a pointer to the line,
* and sets *len to the number of characters, at least 1. The result is
* _not_ a C string; it has no terminating '\0'. The returned pointer
* remains valid until the next call to next_line() or lr_free() with
* the same _lr_.
*
* next_line() returns NULL at end of file, or if there is an error (on
* the stream, or with memory allocation).
*/
char *
next_line(struct line_reader *lr, size_t *len)
{
size_t newsiz;
int c;
char *newbuf;
*len = 0; /* Start with empty line. */
for (;;) {
c = fgetc(lr->f); /* Read next character. */
if (ferror(lr->f))
return NULL;
if (c == EOF) {
/*
* End of file is also end of last line,
` * unless this last line would be empty.
*/
if (*len == 0)
return NULL;
else
return lr->buf;
} else {
/* Append c to the buffer. */
if (*len == lr->siz) {
/* Need a bigger buffer! */
newsiz = lr->siz + 4096;
newbuf = realloc(lr->buf, newsiz);
if (newbuf == NULL)
return NULL;
lr->buf = newbuf;
lr->siz = newsiz;
}
lr->buf[(*len)++] = c;
/* '\n' is end of line. */
if (c == '\n')
return lr->buf;
}
}
}
/*
* Frees internal memory used by _lr_.
*/
void
lr_free(struct line_reader *lr)
{
free(lr->buf);
lr->buf = NULL;
lr->siz = 0;
}
/*
* Read a file line by line.
* http://rosettacode.org/wiki/Read_a_file_line_by_line
*/
int
main()
{
struct line_reader lr;
FILE *f;
size_t len;
char *line;
f = fopen("foobar.txt", "r");
if (f == NULL) {
perror("foobar.txt");
exit(1);
}
/*
* This loop reads each line.
* Remember that line is not a C string.
* There is no terminating '\0'.
*/
lr_init(&lr, f);
while (line = next_line(&lr, &len)) {
/*
* Do something with line.
*/
fputs("LINE: ", stdout);
fwrite(line, len, 1, stdout);
}
if (!feof(f)) {
perror("next_line");
exit(1);
}
lr_free(&lr);
return 0;
}

Resources