In my code I'm trying to read a file, read it's lines and get them into a String array, then print them and close the file. When I run it, it fails on a seg fault and skips the last line of the file, and I just can't find the problem...
My instinct is to blame reading the array wrongly or misbehaving with the file... Am I right?
Any help or redirects would be helpful.
Thank you!
Here is the main file:
#include "files_utils.h"
int main()
{
FILE *fp = fopen("expl", "r");
if (!fp)
return -1;
long lines_count = countlines(fp);
long flen = file_length(fp);
String *lines = calloc(lines_count, sizeof(String));
printf("file length: %ld\n", flen);
printf("file lines: %ld\n", lines_count);
getlines(lines, lines_count, fp);
printf("finished\n");
for (String *sp = lines; sp != NULL; sp++)
printf("%s", *sp);
printf("before close\n");
fclose(fp);
printf("closed\n");
return 0;
}
Here is the files_utils file:
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 10
typedef char *String;
long file_length(FILE *fp)
{
/*
find the length of the file fp points to, regardless of the current position.
*/
long original_pos = ftell(fp), i = 0;
rewind(fp);
// count chars:
for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
i++;
// return the file to it's original position
fseek(fp, original_pos, SEEK_SET);
return i;
}
long countlines(FILE *fp)
{
/*
find the amount of lines in file fp points to, regardless of the current position.
*/
long original_pos = ftell(fp), i = 0;
rewind(fp);
// find newlines:
for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
if (c == '\n')
i++;
// return the file to it's original position
fseek(fp, original_pos, SEEK_SET);
return i;
}
String *getlines(String lines[], long maxlines, FILE *fp)
{
for (int i = 0; i <= maxlines; i++)
{
lines[i] = calloc(MAXLINE, sizeof(char));
fgets(lines[i], MAXLINE, fp);
}
return lines;
}
And it outputs
file length: 144
file lines: 21
finished
... all the lines of the file except of the last one ...
Segmentation fault (core dumped)
The problem was fixed when I changed the printing loop to run until maxlines instead of waiting for NULL. Why is this? Why would waiting for NULL raise a seg fault?
Related
I am trying to apply dynamic memory allocation on reading text files but I don't really get how I could access the contents of the file. I am still having difficulties understanding memory allocation so if it is possible, please explain how I can apply it on file handling.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int counter = 0;
char ch;
char **chpt;
FILE *fp;
fp = fopen("test.txt", "r");
while((ch = fgetc(fp)) != EOF ){
counter++;
}
rewind(fp);
chpt = (char **)malloc(counter * sizeof(char));
fread(chpt, counter * sizeof(char), 1, fp);
for (int i = 0; i < counter; i++){
for (int j = 0; j < counter; j++) {
printf("%c", chpt[i][j]);
}
}
fclose(fp);
free(chpt);
return 0;
}
Your nested for loops don't make any sense and you're trying to print counter*counter characters, but you have only read counter characters. You don't have a 2D array here, and you don't need one either.
Furthermore:
you need to check if fopen fails
the cast with malloc is not needed (but it doesn't harm either)
Your file contains obviously counter characters. So you need to allocate memory for counter characters, read counter characters from the file, and then display the counter characters you've just read.
You want this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int counter = 0;
char ch;
char* chpt; // just a pointer to char
FILE* fp;
fp = fopen("test.txt", "r");
if (f == NULL) // check if fopen failed
{
printf("Can't open file\n"); // print error message
return 1; // and abort
}
while ((ch = fgetc(fp)) != EOF) {
counter++;
}
rewind(fp);
chpt = malloc(counter * sizeof(char)); // no cast needed
fread(chpt, counter * sizeof(char), 1, fp);
for (int i = 0; i < counter; i++) { // one loop is enough
printf("%c", chpt[i]);
}
fclose(fp);
free(chpt);
return 0;
}
There is still room for further improvement:
you should check if malloc fails, even if it's unlikely to fail if the file isn't huge
your method of determining the file size is very inefficient, for more information read this
sizeof(char) is not needed, it is 1 by definition.
Hi I was trying to create an array of string of an undetermined length in c.
This is my code :
int main()
{
int lineCount=linesCount();
char text[lineCount][10];
printf("%d",lineCount);
FILE * fpointer = fopen("test.txt","r");
fgets(text,10,fpointer);
fclose(fpointer);
printf("%s",text);
return 0;
}
I would like to replace 10 in
char text[lineCount][10];
My code reads out a file I already made the amount of lines dynamic.
Since the line length is unpredictable I would like to replace 10 by a something dynamic.
Thanks in advance.
To do this cleanly, we want a char * array rather than an 2D char array:
char *text[lineCount];
And, we need to use memory from the heap to store the individual lines.
Also, don't "hardwire" so called "magic" numbers like 10. Use an enum or #define (e.g) #define MAXWID 10. Note that with the solution below, we obviate the need for using the magic number at all.
Also, note the use of sizeof(buf) below instead of a magic number.
And, we want [separate] loops when reading and printing.
Anyway, here's the refactored code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
linesCount(void)
{
return 23;
}
int
main(void)
{
int lineCount = linesCount();
char *text[lineCount];
char buf[10000];
printf("%d", lineCount);
// open file and _check_ the return
const char *file = "test.txt";
FILE *fpointer = fopen(file, "r");
if (fpointer == NULL) {
perror(file);
exit(1);
}
int i = 0;
while (fgets(buf, sizeof(buf), fpointer) != NULL) {
// strip newline
buf[strcspn(buf,"\n")] = 0;
// store line -- we must allocate this
text[i++] = strdup(buf);
}
fclose(fpointer);
for (i = 0; i < lineCount; ++i)
printf("%s\n", text[i]);
return 0;
}
UPDATE:
The above code is derived from your original code. But, it assumes that the linesCount function can predict the number of lines. And, it doesn't check against overflow of the fixed length text array.
Here is a more generalized version that will allow an arbitrary number of lines with varying line lengths:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
int lineCount = 0;
char **text = NULL;
char buf[10000];
// open file and _check_ the return
const char *file = "test.txt";
FILE *fpointer = fopen(file, "r");
if (fpointer == NULL) {
perror(file);
exit(1);
}
int i = 0;
while (fgets(buf, sizeof(buf), fpointer) != NULL) {
// strip newline
buf[strcspn(buf,"\n")] = 0;
++lineCount;
// increase number of lines in array
text = realloc(text,sizeof(*text) * lineCount);
if (text == NULL) {
perror("realloc");
exit(1);
}
// store line -- we must allocate this
text[lineCount - 1] = strdup(buf);
}
fclose(fpointer);
// print the lines
for (i = 0; i < lineCount; ++i)
printf("%s\n", text[i]);
// more processing ...
// free the lines
for (i = 0; i < lineCount; ++i)
free(text[i]);
// free the list of lines
free(text);
return 0;
}
again. I'm new to C. Still thinking in Python terms (readlines, append them to variable) so I'm having difficulties translating that to C. This is what I want to do: open a text file for reading, store each line in an array row by row, print it out to make sure it's stored.
This is how far I've got:
int main(){
FILE * fp = fopen("sometext.txt", "r");
char text[100][100];
if(fp == NULL){
printf("File not found!");
}
else{
char aLine[20];
int row = 0;
while(fgets(aLine, 20, fp) != NULL){
printf("%s", aLine);
//strcpy(text[row], aLine); Trying to append a line (as row)
return 0;
}
Please don't start with "invest in some more time and look somewhere else because it's easy and has been answered". I'm bad at this, and I'm trying.
You can try this. Basically you need an array of arrays to store each line. You find the length of the longest line in the file and you allocate space for it. Then rewind the pointer to the start of the file and use fgets to get each line from the file and strdup to allocate space and copy the line to the respective position. Hope this helps.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
FILE * fp = fopen("sometext.txt", "r");
int maxLineSize = 0, count = 0;
char c;
while ((c = fgetc(fp)) != EOF) {
if (c == '\n' && count > maxLineSize) maxLineSize = count;
if (c == '\n') count = 0;
count++;
}
rewind(fp);
char ** lines = NULL;
char * line = calloc(maxLineSize, sizeof(char));
for (int i = 0 ; fgets(line, maxLineSize + 1, fp) != NULL ; i++) { // +1 for \0
lines = realloc(lines, (i + 1) * sizeof(char *));
line[strcspn(line, "\n")] = 0; // optional if you want to cut \n from the end of the line
lines[i] = strdup(line);
printf("%s\n", lines[i]);
memset(line, maxLineSize, '\0');
}
fclose(fp);
}
You could solve it without copy
The follow code could work:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <math.h>
int main()
{
FILE * fp = fopen("sometext.txt", "r");
if(fp == NULL){
printf("File not found!");
return -1;
}
char text[100][20];
int row = 0;
while(row < 100 && fgets(text[row], sizeof(text[0]), fp) != NULL)
++row;
for (int i= 0; i != row; ++i)
fputs(text[i], stdout);
return 0;
}
This program attempts to save the contents of a text file into a character variable array. It is then supposed to use my_getline() to print the contents of the character array. I've tested and see that the contents are in fact getting saved into char *text but I can't figure out how to print the contents of char *text using my_getline(). my_getline is a function we wrote in class that I need to use in this program. When I attempt to call it in the way that was taught, it 1 is printed to terminal but then the terminal just waits and nothing else is printed. Any guidance would be appreciated. Also, let me know if I'm missing any information that would help.
/* Include the standard input/output and string libraries */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* Define the maximum lines allowed in an input text and NEWLINE for getline funct. */
#define MAXPATTERN 15
#define MAXFILENAMELENGTH 15
#define NEWLINE '\n'
/* function prototypes */
void my_getline(char text[]);
int find_string(char text[], char pattern[], int length_text, int length_pattern);
int main()
{
FILE *fp;
long lSize;
char *text;
char fileName[MAXFILENAMELENGTH], pattern[MAXPATTERN];
char c;
int length_text, length_pattern, j, lineNumber = 1;
printf("Enter file name: ");
scanf("%s", fileName);
fp = fopen(fileName, "r");
if (fp == NULL)
{
printf("fopen failed.\n");
return(-1);
}
fseek(fp, 0L, SEEK_END);
lSize = ftell(fp);
rewind(fp);
/* allocate memory for all of text file */
text = calloc(1, lSize + 2);
if(!text)
{
fclose(fp);
fputs("memory allocs fails", stderr);
exit(1);
}
/* copy the file into text */
if(1 != fread(text, lSize, 1, fp))
{
fclose(fp);
free(text);
fputs("Entire read fails", stderr);
exit(1);
}
text[lSize + 1] = '\0';
printf("%s has been copied.\n", fileName);
rewind(fp);
printf("%d ", lineNumber);
for (j = 0; (j = getchar()) != '\0'; j++)
{
my_getline(text);
printf("%d %s\n", j+1, text);
}
printf("Enter the pattern you would like to search for: ");
scanf("%s", pattern);
printf("\nYou have chosen to search for: %s\n", pattern);
fclose(fp);
free(text);
return(0);
}
void my_getline(char text[])
{
int i = 0;
while ((text[i] = getchar()) != NEWLINE)
++i;
text[i] = '\0';
}
Your function is causing a system hang because you're calling getchar(), which returns the next character from the standard input. Is this really what you want?
At this point, your program is expecting input from the user. Try typing in the console windows and pressing to see it coming back from the "hang"
It is most likely causing an infinite loop because you are not checking whether you have reached EOF.
void my_getline(char text[])
{
int i = 0;
int c;
while ( (c = getchar()) != NEWLINE && c != EOF )
text[i++] = c;
text[i] = '\0';
}
I have written some code to read each lines of textfile to 2d array.
/* FileProcess.c library */
#define LINE_SIZE 128 /* Max line's length = 256 characters */
extern ulong
File_ReadLine (FILE *fptr,
char **result)
{
char buff_line[LINE_SIZE], *p;
ulong nLines = 0UL;
/* Check if fptr is readable */
if (fptr == NULL) {
printf("File not found.\n");
return -1;
}
/*get number of lines; from http://stackoverflow.com/a/3837983 */
while (fgets(buff_line, LINE_SIZE, fptr))
if (!(strlen(buff_line) == LINE_SIZE-1 && buff_line[LINE_SIZE-2] != '\n'))
nLines++;
/* Allocating memory for result */
result = malloc(nLines * sizeof(char *)); //
/* Pointer return to begin of file */
rewind(fptr);
/* Getting lines */
int i = 0;
while (!feof(fptr)) {
/* Get current line to buff_line */
fgets(buff_line, LINE_SIZE, fptr);
/* Replace '\n' at the end of line */
char *c = strchr(buff_line, '\n');
if (c != NULL)
*c = '\0';
/* Handle '\n' at the end of file */
if (feof(fptr))
break;
/* Memory allocate for p */
result[i] = malloc (LINE_SIZE * sizeof(char));
/* Copy buff_line to p */
strcpy(result[i], buff_line);
i++;
}
return (nLines);
}
main program:
int main ()
{
char **Phone;
FILE *fptr;
fptr = fopen("phone.na.txt", "r");
ulong nLines = File_ReadLine(fptr, Phone);
printf("%ld\n", nLines);
int i;
for (i = 0; i < nLines; i++) {
printf("%s", Phone[i]);
}
fclose(fptr);
return 1;
}
Using gdb, running line by line, program return segmentation fault after
printf("%s", Phone[i]);
So I can't understand why segmentation fault here? Are there any errors with malloc() ?
I haven't compiled or run the code, but I think the problem is in your line counter:
while (fgets(buff_line, LINE_SIZE, fptr))
if (!(strlen(buff_line) == LINE_SIZE-1 && buff_line[LINE_SIZE-2] != '\n'))
nLines++;
What you're saying here is unless "the string length of buff_line is equal to LINE_SIZE -1 and the character at buff_line[LINE_SIZE-1] is not equal to '\n'", increment nLines.
So... whenever you read a line out of your text file which ends with '\n', and that line is 127 characters long, you're not going to increment nLines. You malloc spaces for nLines, but you're probably going to read more than nLines of data from your file... at that point, you're writing more into **result than you have allocated, and bad things are going to happen.