I'm trying to read a file (full of a word followed by a newline), to an array full of pointers to each string. Then print each word in the array, and count the number of words read. However it just prints no words and says 0 words imported.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define ARGS_REQUIRED 2
#define MAX_WORDS 50
#define MAX_WORD_LENGTH 1024
void read_file (char * argv[], char word_storage[]);
void usage (char * argv[]);
int main (int argc, char* argv[])
{
char word_storage[MAX_WORDS];
if (argc == ARGS_REQUIRED)
{
system("clear");
read_file(&argv[1], word_storage);
}
else
{
usage(&argv[0]);
}
return 0;
}
void usage (char * argv[])
{
printf("Incorrect usage, try: ./program_name %s\n", argv[1]);
}
void read_file (char * argv[], char word_storage[])
{
FILE * file_name;
char *word[MAX_WORDS][MAX_WORD_LENGTH];
int word_count = 0, i = 0, j;
if ((file_name = fopen(argv[0], "r")) == NULL)
{
printf("Cannot open file ... \n");
}
while (fscanf(file_name, "%s", *word[MAX_WORDS]) == 1)
{
for (j = 0; j < MAX_WORDS; j++)
{
if (printf("%s\n", *word[j]) == 1)
{
word_count++;
}
}
}
fclose(file_name);
printf("Imported words: %d\n", word_count);
}
You can modify your program as below. It worked for me. Please add error check for printf() function.
void read_file (char * argv[], char word_storage[])
{
FILE * file_name;
char word[MAX_WORDS][MAX_WORD_LENGTH];
int word_count = 0, i = 0, j;
if ((file_name = fopen(argv[0], "r")) == NULL)
{
printf("Cannot open file ... \n");
}
while (fscanf(file_name, "%s", word) == 1)
{
printf("%s\n",word);
word_count++;
/*
for (j = 0; j < MAX_WORDS; j++)
{
if (printf("%s\n", word[j]) == 1)
{
word_count++;
}
}
*/
}
fclose(file_name);
printf("Imported words: %d\n", word_count);
}
Related
I have a working example of copy lines from a file into an array of strings. I want to move the code to copy the lines into a function to which I simply pass a pointer to the array of strings, where the lines will be stored, and a pointer to the file. However, I have tried to move the code into a function and keep getting seg faults. I have tried debugging using GDB and it seems like the problem is with the memory allocation to rows. But I can't work out what the problem is. realloc seems to be working correctly since I find the size of row increases on the 3rd iteration (using malloc_usable_size(*rows)), but then seg faults. I'm compiling with gcc -Wall -Wextra -pedantic -std=c99 -g c_programs/read_file_function.c on Linux.
Working example
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Please supply a file path:\n%s <file path>\n", argv[0]);
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (!fp)
{
perror("ERROR");
return EXIT_FAILURE;
}
char **rows = (char **)malloc(sizeof(char *));
char *lineBuf = NULL;
size_t n = 0;
size_t nLines = 0;
ssize_t lineLength = 0;
size_t i = 0;
while ((lineLength = getline(&lineBuf, &n, fp)) != -1)
{
lineBuf[strcspn(lineBuf, "\n")] = 0;
lineBuf[strcspn(lineBuf, "\r")] = 0;
rows[i] = (char *)malloc(lineLength + 1);
strcpy(rows[i], lineBuf);
i++;
nLines = i;
rows = (char **)realloc(rows, (nLines + 1) * sizeof(char *));
}
printf("nLines: %lu\n", nLines);
printf("row 1: %s\n", rows[0]);
printf("row 2: %s\n", rows[1]);
printf("row 2: %s\n", rows[10]);
return 0;
}
Non working function version
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t readFile(FILE **fp, char ***rows)
{
char *lineBuf = NULL;
size_t n = 0;
size_t nLines = 0;
ssize_t lineLength = 0;
size_t i = 0;
while ((lineLength = getline(&lineBuf, &n, *fp)) != -1)
{
lineBuf[strcspn(lineBuf, "\n")] = 0;
lineBuf[strcspn(lineBuf, "\r")] = 0;
*rows[i] = (char *)malloc(lineLength + 1);
strcpy(*rows[i], lineBuf);
i++;
nLines = i;
*rows = (char **)realloc(*rows, (nLines + 1) * sizeof(char *));
}
return nLines;
}
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Please supply a file path:\n%s <file path>\n", argv[0]);
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (!fp)
{
perror("ERROR");
return EXIT_FAILURE;
}
char **rows = (char **)malloc(sizeof(char *));
size_t nLines = readFile(&fp, &rows);
printf("nLines: %lu", nLines);
printf("row 1: %s", rows[0]);
printf("row 2: %s", rows[1]);
return 0;
}
*rows[i] is doing *(rows[i]) - accessing ith element in the array of rows, and then dereferencing it. You want to do (*rows)[i] - dereference rows and then access ith element.
I advise to:
readFile(..., char ***rows0) {
char **rows = NULL; // temporary internal variable
...
// use rows normally
rows = stuff();
...
// when finished, assign once
*rows0 = rows;
return nLines;
}
But do not be a 3-star programmer. At best, use a structure, -> is easy to use. Like:
struct string {
char *str;
};
struct lines {
struct string *strs;
size_t cnt;
};
// #return 0 on success, otherwise error
int readFile(...., struct lines *p) {
// initialization
p->cnt = 0;
p->strs = NULL;
...
void *pnt = realloc(p->strs, (p->cnt + 1) * ....);
if (!pnt) { /* handle error */ return -1; }
p->strs = pnt;
p->strs[p->cnt]->str = malloc(lineLenght + 1);
if (!p->strs[p->cnt]->str) { /* handle error */ return -2; }
strcpy(p->strs[p->cnt]->str, lineBuf);
p->cnt++;
...
return 0; /* success */
}
int main(int argc, char **argv) {
struct lines p = {0};
if (readFile(..., &p)) {
/* handle error */
}
printf("nLines: %zu\n", p.cnt);
Do not pre-allocate memory. Initialize memory with NULL and call realloc before using memory. realloc(NULL is the same as malloc().
Check for allocation errors.
I have created functions that are supposed to find the number of lines in a file (find_numlines()) and a function to read the lines of the file into char*** lines (read_lines()). The rest of the functions in my main were provided so the problems are not in those functions.
read_lines.c (UPDATED):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "read_lines.h"
int findnum_lines(FILE* fp){
int num_lines = 0;
int line;
line = getc(fp);
if (line != EOF) {
num_lines++;
do {
if (line == '\n') {
num_lines = num_lines + 1;
}
line = getc(fp);
}
while (line != EOF);
}
rewind(fp);
return num_lines;
}
void read_lines(FILE* fp, char*** lines, int* num_lines){
int i;
(*lines) = malloc(*num_lines * sizeof(char*));
for (i=0; i < *num_lines; i++)
{
(*lines)[i] = malloc(1000);
(*lines)[i][0] = '\0';
fgets((*lines)[i], 1000, fp);
}
}
main.c :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "read_lines.h"
void print_lines(char** lines, int num_lines){
int i;
for(i = 0 ; i < num_lines; ++i){
printf("%d. %s", i+1, lines[i]);
}
}
void free_lines(char** lines, int num_lines){
int i;
for(i = 0 ; i < num_lines; ++i){
free(lines[i]);
}
if(lines != NULL && num_lines > 0){
free(lines);
}
}
FILE* validate_input(int argc, char* argv[]){
FILE* fp = NULL;
if(argc < 2){
printf("Not enough arguments entered.\nEnding program.\n");
exit(0);
}
else if(argc > 2){
printf("Too many arguments entered.\nEnding program.\n");
exit(0);
}
fp = fopen(argv[1], "r");
if(fp == NULL){
perror("fopen");
printf("Unable to open file: %s\nEnding program.\n", argv[1]);
//fprintf(stderr, "Unable to open file %s: %s\n", argv[1], strerror(errno));
exit(0);
}
return fp;
}
int main(int argc, char* argv[]){
char** lines = NULL;
int num_lines = 0;
FILE* fp = validate_input(argc, argv);
num_lines = findnum_lines(fp);
read_lines(fp, &lines, &num_lines);
print_lines(lines, num_lines);
free_lines(lines, num_lines);
fclose(fp);
return 0;
}
read_lines.h :
#ifndef READ_LINES
#define READ_LINES
#include <stdio.h>
void read_lines(FILE* fp, char*** lines, int* num_lines);
int findnum_lines(FILE* fp);
#endif
Whenever I input a file the find_numlines() returns the correct number of lines but something goes wrong in the read_lines() because lines is still NULL.
The example file is normal.txt :
Hello Class
This is what I would call a normal file
It isn't very special
But it still is important
The ouptut should be:
1. Hello Class
2. This is what I would call a normal file
3. It isn't very special
4. But it still is important
In the following code I added the rewind command (mentioned by xing) and the memory allocation for each line and the "line table". Further improvements were performed in the code for counting the lines and the error handling.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int findnum_lines(FILE* fp){
int num_lines = 0;
int c;
c = getc(fp);
if (c != EOF) {
num_lines++;
do {
if (c == '\n') {
num_lines = num_lines + 1;
}
c = getc(fp);
}
while (c != EOF);
}
rewind(fp);
return num_lines;
}
void read_lines(FILE* fp, char*** lines, int* num_lines){
int i;
// allocate memory for pointers to start of lines
(*lines) = malloc(*num_lines * sizeof(char*));
for (i=0; i < *num_lines; i++)
{
(*lines)[i] = malloc(1000);
(*lines)[i][0] = '\0'; // terminate for the case that last line does not contain characters
fgets((*lines)[i], 1000, fp); // read up to 999 characters and terminate string
}
}
void print_lines(char** lines, int num_lines){
int i;
for(i = 0 ; i < num_lines; ++i){
printf("%d. %s", i+1, lines[i]);
}
printf("\n");
}
void free_lines(char** lines, int num_lines){
int i;
for (i = 0 ; i < num_lines; ++i) {
if (lines[i]!=NULL) {
free(lines[i]);
}
}
if (lines != NULL){
free(lines);
}
}
FILE* validate_input(int argc, char* argv[]){
FILE* fp = NULL;
if (argc < 2){
printf("Not enough arguments entered.\n");
}
else if (argc > 2){
printf("Too many arguments entered.\n");
}
else {
fp = fopen(argv[1], "r");
if (fp == NULL){
printf("Unable to open file: %s\n", argv[1]);
}
}
return fp;
}
int main(int argc, char* argv[]){
char** lines = NULL;
int num_lines = 0;
FILE* fp = validate_input(argc, argv);
if (fp != NULL)
{
num_lines = findnum_lines(fp);
read_lines(fp, &lines, &num_lines);
print_lines(lines, num_lines);
free_lines(lines, num_lines);
fclose(fp);
}
return 0;
}
I wrote the next function that tries to read and enter each line from text file into a string array in c :
int main(int argc,char* argv[])
{
char ** lines;
readFile(argv[1],lines);
}
int readFile(char* filePath,char** lines)
{
char file_char;
int letter_in_line=0;
int line=1;
char* line_string=malloc(1024);
int j=1;
int fd=open(filePath,O_RDONLY);
if (fd < 0)
{
return 0;
}
while (read(fd,&file_char,1) >0)
{
if(file_char != '\n' && file_char != '0x0')
{
line_string[letter_in_line] = file_char;
letter_in_line++;
}
else
{
if(lines != NULL)
{
lines=(char**)realloc(lines,sizeof(char*)*line);
}
else
{
lines=(char**)malloc(sizeof(char*));
}
char* line_s_copy=strdup(line_string);
lines[line-1]=line_s_copy;
line++;
letter_in_line=0;
memset(line_string,0,strlen(line_string));
}
j++;
}
printf("cell 0 : %s",lines[0]);
return 1;
}
I have 2 questions :
1)Whenever the code reaches the print of cell 0, I'm getting
Segmentation fault (core dumped) error. What is wrong ?
2)In case I
want to see the changes in the lines array in my main, I should pass
&lines to the func and get char*** lines as an argument ? In
addition, I will need to replace every 'line' keyword with '*line' ?
*I know that I can use fopen,fget, etc... I decided to implement it in this way for a reason.
There is many issues that make your code core dump.
Here a version very similar to your code. I hope it will help you to understand this.
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
int read_file(const char *filename, char ***result)
{
/* open the file */
const int fd = open(filename, O_RDONLY);
if (fd < 0) {
*result = NULL;
return -1;
}
/* read the file characters by characters */
char *buffer = (char *)malloc(sizeof(char) * 1024);
char c;
int column = 0;
int line = 0;
*result = NULL;
/* for each characters in the file */
while (read(fd, &c, 1) > 0) {
/* check for end of line */
if (c != '\n' && c != 0 && column < 1024 - 1)
buffer[column++] = c;
else {
/* string are null terminated in C */
buffer[column] = 0;
column = 0;
/* alloc memory for this line in result */
*result = (char **)realloc(*result, sizeof(char *) *
(line + 1));
/* duplicate buffer and store it in result */
(*result)[line++] = strdup(buffer);
}
}
free(buffer);
return line;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: %s [filename]", argv[0]);
return 1;
}
char **lines;
int line_count = read_file(argv[1], &lines);
if (line_count < 0) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
for(int i=0; i < line_count; i++)
printf("%s\n", lines[i]);
return 0;
}
Here an other version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int read_file(const char *filename, char ***result)
{
/* init result */
*result = NULL;
/* open the file */
FILE *file = fopen(filename, "r");
if (file == NULL)
return -1;
/* read the file line by line */
char *buffer = (char *)malloc(sizeof(char) * 1024);
int line = 0;
while (fgets(buffer, 1024, file)) {
*result = (char **)realloc(*result, sizeof(char *) *
(line + 1));
(*result)[line++] = strdup(buffer);
}
free(buffer);
return line;
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "usage: %s [filename]", argv[0]);
return 1;
}
char **lines;
int line_count = read_file(argv[1], &lines);
if (line_count < 0) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
for(int i=0; i < line_count; i++)
printf("%s\n", lines[i]);
return 0;
}
I have an unknown segfault within my print function when I call it in main and I can't see what the obvious fix is. I have put printf's throughout the program and it doesn't print 'here4' making me think it's due to my print function, or when I call it in main.
I want to read a dictionary file into an array of strings.
Here is a snippet of the code:
Any ideas would be greatly appreciated, thanks.
#define PRIME 1009
void fileRead(int argc, char **argv)
void printTable(int arrayLength, char **table);
int main(int argc, char **argv)
{
char **table;
FILE *fp;
int i, arrayLength = PRIME;
/* Initial memory allocation */
table = (char**)malloc(PRIME*sizeof(char));
fileRead(argc, argv);
printf("here3\n");
for(i = 0; i < arrayLength; i++) {
printTable(arrayLength,table);
}
printf("here4\n");
return 0;
}
void fileRead(int argc, char **argv)
{
FILE *fp;
char *word;
int arrayLength = PRIME;
word = calloc(MAXCHAR, sizeof(char));
fp = fopen (argv[1], "r");
printf("here1\n");
/*read in grid and move along a cell each time */
while (fscanf(fp, "%s", word)!= EOF) {
if (argc != (2)) {
fprintf(stderr, "Cannot open file, %s\n Try again e.g. %s dictionary.txt\n" , argv[1], argv[0]);
}
if(fp == NULL) {
fprintf(stderr, "Cannot open file, %s\n Try again e.g. %s dictionary.txt\n" , argv[1], argv[0]);
return;
}
if (fp == NULL) {
fprintf(stderr, "Error opening file, try file name dictionary.txt\n");
exit(EXIT_FAILURE);
}
}
printf("here2\n");
fclose(fp);
return;
}
void printTable(int arrayLength, char **table)
{
int i;
for(i = 0; i < arrayLength; i++) {
printf("%s\n", table[i]);
}
printf("\n");
}
Let me summarize your code:
you allocate uninitialized memory for table
You call a function fileRead():
Allocate some memory for word
read the file
Do nothing with the data read.
fileRead() does nothing useful: It does not return anything, it doesn't touch table, is vulnerable to a buffer overflow of word and leaves the memory leak of word behind.
And then you printf the unchanged and uninitialized content of table
try this
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PRIME 1009
#define MAXCHAR 256
char **fileRead(FILE *fp, int *len);
void printTable(int arrayLength, char **table);
int main(int argc, char **argv){
if (argc != 2) {
fprintf(stderr, "Need dictionary file argument.\nTry again e.g. %s dictionary.txt\n" , argv[0]);
exit(EXIT_FAILURE);
}
FILE *fp = fopen (argv[1], "r");
if(fp == NULL) {
fprintf(stderr, "Cannot open file, %s\nTry again e.g. %s dictionary.txt\n" , argv[1], argv[0]);
exit(EXIT_FAILURE);
}
int arrayLength = PRIME;
char **table = fileRead(fp, &arrayLength);//fclose(fp) inside this
printTable(arrayLength, table);
for(int i = 0; i < arrayLength; ++i)
free(table[i]);
free(table);
return 0;
}
char **fileRead(FILE *fp, int *len){
char *word = calloc(MAXCHAR, sizeof(char));
char **table = malloc(*len * sizeof(char*));
int i = 0;
while (i < *len && fscanf(fp, "%s", word) != EOF){
table[i] = malloc(strlen(word)+1);
strcpy(table[i++], word);
}
fclose(fp);
*len = i;
free(word);
return table;
}
void printTable(int arrayLength, char **table){
int i;
for(i = 0; i < arrayLength; i++) {
printf("%s\n", table[i]);
}
printf("\n");
}
I have no idea what is wrong here. Trying to scan a list of names:
bob
tim
tony
alison
jim
etc. into double pointer **strings but I keep getting a seg fault dont see where.
void insert_data(char **strings, const char *filename, int size)
{
int j = 0;
FILE* file = fopen(filename, "r");
if(file == NULL)
{
printf("File could not be opened");
return;
}
for(j=0; j<size; j++)
{
fscanf(file,"%s", strings[j]);
printf("%s\n", strings[j]);
}
fclose(file);
}
I have a separate function to allocate memory but it still seg faults
void allocate(char ***strings, int size)
{
strings = malloc(size * sizeof(char*));
if(strings == NULL)
{
printf("Could not allocate memory\n");
}
int i;
for(i=0;i<size;i++)
{
*(strings+i) = malloc(MAX_STRING_LEN * sizeof(char));
if(strings == NULL)
{
printf("Could not allocate memory\n");
}
}
}
The core of your function is more or less sound; the problem is more likely in the way you invoke it than in the function itself. The following code, which has only minor fixes to the code in the function, works OK.
#include <stdio.h>
static int insert_data(char **strings, const char *filename, int size)
{
int j = 0;
FILE *file = fopen(filename, "r");
if (file == NULL)
{
fprintf(stderr, "File %s could not be opened\n", filename);
return 0;
}
for (j = 0; j < size; j++)
{
if (fscanf(file, "%s", strings[j]) != 1)
return j;
printf("%s\n", strings[j]);
}
fclose(file);
return size;
}
int main(int argc, char **argv)
{
char data[10][20];
char *str[10];
if (argc != 2)
{
fprintf(stderr, "Usage: %s file\n", argv[0]);
return 1;
}
for (int i = 0; i < 10; i++)
str[i] = data[i];
int n = insert_data(str, argv[1], 10);
for (int i = 0; i < n; i++)
printf("%d: [%s] [%s]\n", i, str[i], data[i]);
// Invalid - incompatible pointer type!
// int m = insert_data(data, "data2", 10);
return 0;
}
Note that given the function prototype, you must pass an array of pointers, rather than trying to pass a pointer to a 2D array of characters. The compiler warns if you try to misuse the function.
change to
#define _S(x) #x
#define S(x) _S(x)
void insert_data(char **strings, const char *filename, int size)
{
int j = 0;
FILE* file = fopen(filename, "r");
if(file == NULL)
{
printf("File could not be opened");
return;
}
for(j=0; j<size; j++)
{
fscanf(file, "%" S(MAX_STRING_LEN) "s", strings[j]);//E.G. #define MAX_STRING_len 64
printf("%s\n", strings[j]);
}
fclose(file);
}
void allocate(char ***strings, int size)
{
*strings = malloc(size * sizeof(char*));
if(*strings == NULL)
{
printf("Could not allocate memory\n");
return ;
}
int i;
for(i=0;i<size;i++)
{
(*strings)[i] = malloc((MAX_STRING_LEN+1) * sizeof(char));
if((*strings)[i] == NULL)
{
printf("Could not allocate memory\n");
return ;
}
}
}