C Programming - Read line from file and put into array [duplicate] - arrays

This question already has answers here:
Getting a stack overflow exception when declaring a large array
(8 answers)
Closed 1 year ago.
I have quite a big dictionary file. I want to take each line from the file and store it in an array so I can perform manipulations later. For example given the words:
aaaa
arggghhh
broooooo
Coooodee
If I call array[2], it should give me "broooooo". I have tried using the code below however I keep running into segmentation faults. Any help would be greatly appreciated.
Here is the code I have been trying:
int main (int argc, char* argv[]) {
char* file="/usr/share/dict/words";
FILE *dict;
char str[60];
char arr[80368][60];
int count = 0;
dict = fopen(file, "r");
if(dict == NULL){
perror("Error opening file");
return(-1);
}
while(fgets(str,sizeof(str),dict) != NULL){
strcpy(arr[count], str);
count++;
}
fclose(dict);
return 0;
}

char arr[80368][60];
This will try to allocate 4822080 bytes on the stack. The default maximum should be 8Mb so it is lower than that, but maybe it is configured lower on your system? You can inspect with ulimit -a | grep stack.
Does your program work if you test with a smaller input file, say generated with head -100 /usr/share/dict/words > input.txt, and then with the size of arr reduced correspondingly?

This is the best method to read file when you don't know the size.
If you want the file inside array you just need to use str_split else the file is inside char *
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <fcntl.h>
#include <stddef.h>
char **str_split(char *cmd, char split_by)
{
char **argv = malloc(sizeof(char *) * strlen(cmd));
int pos = 0;
for (int i = 0; cmd[i] != '\0'; i++) {
if (cmd[i] == split_by || cmd[i] == '\0') {
cmd[i] = '\0';
argv[pos] = strdup(cmd);
pos++;
cmd += i + 1;
i = 0;
}
}
argv[pos] = strdup(cmd);
argv[pos + 1] = NULL;
return argv;
}
char *load_file(char *file_path)
{
int fd;
struct stat file_stat;
stat(file_path, &file_stat);
int file_size = file_stat.st_size;
char *str = malloc(sizeof(char) * file_size + 2);
if ((fd = open(file_path, O_RDONLY)) == -1) {
printf("error");
exit(84);
}
read(fd, str, file_size);
str[file_size] = '\0';
return str;
}
int main(int ac, char **av)
{
char *file_in_string = load_file(av[1]);
printf("this is the file in one string:\n%s\n", file_in_string);
char **file_in_array = str_split(file_in_string, '\n');
printf("this is the file inside array");
for (int i = 0; file_in_array[i]; i++)
printf("line [%d]: %s\n", i, file_in_array[i]);
}

Related

How would I create an array of char* after reading an unknown number of strings (each of unknown length) from a file? [duplicate]

This question already has answers here:
How should character arrays be used as strings?
(4 answers)
Closed 12 months ago.
I have a file with an unknown number of strings and each of these strings is of an unknown length.
I would like to make each line of the file its own string in an array of strings.
I tried to use dynamic allocation in a char** array, but I don't think I'm approaching this correctly.
Below is the code I have tried. It's getting stuck in an infinite loop, and I can't figure out why.
(The text file I'm reading from ends with a line break, by the way.)
#include <getopt.h> //for getopts
#include <sys/stat.h> //to do file stat
#include <dirent.h>
#include <string.h>
#include <pwd.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h> //user macros
#include <stdlib.h>
#include <stdbool.h>
#include <libgen.h>
#include <errno.h>
int main(int argc, char *argv[]) {
//storing the filename inside string
char* filename = argv[1];
FILE *fp1 = fopen(filename, "r");
if (fp1 == NULL) {
fprintf(stderr, "Error: Cannot open '%s'. No such file or directory.\n", filename);
return EXIT_FAILURE;
}
/**
* we begin by getting the number of numbers in the file
* the number of numbers = number of lines = number of line breaks
*/
size_t numNumbers = 0;
// while((fscanf(fp1, "%*[^\n]"), fscanf(fp1, "%*c")) != EOF){
// numNumbers = numNumbers + 1;
// }
char c;
while((c = fgetc(fp1)) != EOF){
if(c == '\n'){
numNumbers++;
}
}
fclose(fp1);
FILE *fp2 = fopen(filename, "r");
char** arrayOfStrings = malloc(numNumbers * sizeof(char*));
for(int i = 0; i < numNumbers; i++) {
int len = 0;
if(((c = fgetc(fp1)) != '\n') && (c != EOF)){
len++;
}
arrayOfStrings[i] = malloc(len * sizeof(char));
}
printf("hello1\n");
//for(int i = 0; i < numNumbers; i++){
// fscanf(fp2, "%s", (arrayOfStrings[i]));
//}
fclose(fp2);
// for(int i = 0; i < numNumbers; i++){
// fprintf(stdout, "%s", arrayOfStrings[i]);
// }
return 0;
}
(I'm very new to C, so please go easy on me!)
In C, strings are terminated with a '0' byte, so it looks like your malloc for each string is 1 character too short -- you've only allowed space for the text.
In addition, you mean the count for the size of each line to be a while loop, not an if statement - right now you are counting each line as length "1".
Finally, you are reading off the end of the file in your commented out fscanf code because you haven't closed and reopened it.
Assuming you want to split the input to the strings by the newline character, would you please try:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *filename; // filename to read
char **arrayOfStrings = NULL; // array of strings
char line[BUFSIZ]; // line buffer while reading
char *p; // temporal pointer to the input line
int i, num; // counter for lines
FILE *fp; // file pointer to read
if (argc != 2) {
fprintf(stderr, "usage: %s file.txt\n", argv[0]);
return EXIT_FAILURE;
}
filename = argv[1];
if (NULL == (fp = fopen(filename, "r"))) {
perror(filename);
return EXIT_FAILURE;
}
// read the input file line by line
while (fgets(line, BUFSIZ, fp)) {
if ((p = strrchr(line, '\n'))) *p = '\0'; // remove trailing newline, if any
if ((p = strrchr(line, '\r'))) *p = '\0'; // remove trailing cr character, if any
if (NULL == (arrayOfStrings = realloc(arrayOfStrings, (num + 1) * sizeof(char **)))) {
// enlarge the array according to the line count
perror("realloc");
return EXIT_FAILURE;
}
if (NULL == (arrayOfStrings[num] = malloc(strlen(line) + 1))) {
// memory for the string of the line
perror("malloc");
return EXIT_FAILURE;
}
strcpy(arrayOfStrings[num], line);
num++;
}
// print the strings in the array
for (i = 0; i < num; i++) {
printf("%d %s\n", i, arrayOfStrings[i]);
}
fclose(fp);
return 0;
}
If the input file looks something like:
This
is
the
input.
Then the output will be:
0 This
1 is
2 the
3 input.

Segmentation fault reading random lines of text?

I have read a lot documentation about this subject but i still have some problem about this,ı know what pointer is but when i try to use ı am facing some problem ,at below code,txt file includes just one words at every line.I tried the read random line from text and return to main function cause after i will need this).And i print it in main function ,please can you help me which section should i change in this code?(When ı try to run this the error message is Segmentation fault (core dumped))
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
char word(char *file, char str[], int i);
int main() {
char buf[512];
char j = word("words.txt", buf, 512);
puts(j); // print random num
}
char word(char *file, char str[], int i) {
int end, loop, line;
FILE *fd = fopen(file, "r");
if (fd == NULL) {
printf("Failed to open file\n");
return -1;
}
srand(time(NULL));
line = rand() % 100 + 1; // take random num
for (end = loop = 0; loop < line; ++loop) {
if (0 == fgets(str, sizeof(str), fd)) { // assign text within a random line to STR
end = 1;
break;
}
}
if (!end)
return (char*)str; // return the str pointer
fclose(fd);
}
You reopened the file, this might be the case. You don't close the file if it returns in if(!end) part the fd is not closed.
And the function takes char but actually needs char *
char word(char *file, char str, int i);
int main() {
char * buf = malloc(sizeof(char)*512);
char *words = "words.txt";
char* j = word(words, buf, 512);
puts(j); // print random num
}
char word(char *file, char str[], int i) { // FIX HERE
int end, loop, line;
FILE *fd = fopen(file, "r"); //
if (fd == NULL) {
printf("Failed to open file\n");
return -1;
}
srand(time(NULL));
line = rand() % 100 + 1; // take random num
for (end = loop = 0; loop < line; ++loop) {
if (0 == fgets(str, sizeof(str), fd)) { // MAIN PROBLEM, PUT A CHAR* TO STR.
end = 1;
break;
}
}
fclose(fd); // YOU DIDN'T CLOSE IF IT RETURNED BEFORE
if (!end)
return str; // return the str pointer
//NOTHING IS RETURNED HERE
return str;
// I guess the problem is here, you return nothing and the function finishes, and you try to write that nothing with puts function which may cause a seg fault.
}```

How can I use fgets in VS

I've been trying to run this code on VS2017. The code is compiling and running, but not in the way I want it too. So, I try to use the debugger and it says:
Debug Assertion Failed!
Program:
File: minkernel\crts\ucrt\src\appcrt\stdio\fgets.cpp
Line:33
Expression: stream.valid()
From past questions I understood that it may happen because of mishandling the opening of files, but I think that my code does take care of it.
Any help would be greatly appreciated!
(my relevant code):
int main(int argc, char *argv[]) {
int i, count_commands, PC_A, lastLine;
int *PC = &PC_A;
FILE *memin;
FILE *memout;
FILE *regout;
FILE *trace;
FILE *count;
assert(argc == 6);
*PC = 0;
count_commands = 0;
//allocationg memory for registers content
char **regs = (char **)(malloc(sizeof(char *) * 16));
for (i = 0; i < 16; i++) {
regs[i] = (char *)(malloc(sizeof(char) * 9));
for (int j = 0; j < 8; j++) {
regs[i][j] = '0';
}
regs[i][8] = '\0';
}
//allocationg memory for the memory image we have
char **memory = (char **)(malloc(sizeof(char *) * 4096));
for (i = 0; i < 4096; i++) {
memory[i] = (char *)(malloc(sizeof(char) * 9));
memory[i][0] = '\0';
}
//load memin image into memory
char *line = (char *)malloc(sizeof(char) * 8);
memin = fopen(argv[1], "r");
if (memin != NULL) {
perror(strerror(errno));
}
int j = 0;
while ((line = fgets(line, 10, (FILE *)memin)) != NULL) {
strcpy(memory[j], line);
memory[j][8] = '\0';
j++;
}
After opening the file, in OP's code there is this check:
if (memin != NULL) {
perror(strerror(errno));
}
So, if the opening succeeded an error string is printed. In my implementation, it reports:
Success: Success
No action is taken if it fails to open the file.
When it comes to the actual reading of all the lines in the file, there are some other issues. A buffer (char array) named line of size 8 is dinamically allocated and passed to fgets:
while ((line = fgets(line, 10, (FILE *)memin)) != NULL) {
// ^^
Note that 10 is also passed, as size of the buffer, which is wrong, because it allows fgets to write out of the bounds of the allocated array.
Also, given OP's compiler is MSVC 2017, I assume this code is running on Windows, so chances are that in the file, the lines are terminated by a "\r\n" sequence, rather then a single '\n'. Even if OP is confident that each line is a 8 char string, fgets needs a buffer of at least size 8 + 3 (8 + '\r' + '\n' + '\0') to read them safely.
Consider how those suggestions are implemented in this snippet:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define MEM_SIZE 1024u
#define LINE_SIZE 128u
#define STR_SIZE 8u
int main(int argc, char *argv[])
{
// Try to open the input file
if (argc < 2) {
fprintf(stderr, "Missing file name in command line.\n");
return EXIT_FAILURE;
}
FILE *memin = fopen(argv[1], "r");
if (memin == NULL) {
fprintf(stderr, "Unable to open file [%s].\n", argv[1]);
return EXIT_FAILURE;
}
// I'd use plain arrays to store the lines
char memory[MEM_SIZE][STR_SIZE + 1] = {{'\0'}};
char line[LINE_SIZE] = {'\0'};
size_t count = 0;
while ( count < MEM_SIZE && fgets(line, LINE_SIZE, memin) ) {
size_t length = strcspn(line, "\r\n");
if (length > STR_SIZE) {
fprintf(stdout, "Warning, line too long: %zu.\n", count);
length = STR_SIZE;
}
memcpy(memory[count], line, length);
memory[count][STR_SIZE] = '\0';
++count;
}
for ( size_t i = 0; i < count; ++i ) {
printf("[%s]\n", memory[i]);
}
}

segmentation fault caused by 2d array or strtok

I am trying to create a thread and read from stdin inside the thread. In main() have dynamically allocated memory to a 2d array based on the size given as user input. In the thread I am reading from stdin and splitting it using strtok and adding it into the 2d array. I am not sure why there is a segmentation fault, searched SO and I seem to have handled all the cases related to strtok.
This is the program -
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int dimB;
int dimA;
char** buffer;
char* temp;
void *thread(void *threadid){
char *buf;//[30] = {};
size_t len = 0;
ssize_t read;
char *line = NULL;
char *each;
printf("Hello World!.\n");
while ((read = getline(&line, &len, stdin)) != -1) {
printf("%s || \n", line);
each = strtok(line," ,()");
printf("************************%s ", each);
while(each != NULL){
buf = each;
strcpy(buffer[0][0], buf);
printf("%s", buf);
each = strtok(NULL," ,()");
}
}
pthread_exit(NULL);
}
int main (int argc, char *argv[])
{
pthread_t tidMpr;
long r;
int i;
dimB = atoi(argv[1]);
dimA = atoi(argv[2]);
pthread_t tidRdr[dimA];
buffer = malloc(dimA * sizeof(char*));
temp = malloc(dimA * dimB * sizeof(char));
for (i = 0; i < dimA; i++) {
buffer[i] = temp + (i * dimB);
}
//Create thread Thread
pthread_create(&tidMpr, NULL, thread, NULL);
free(temp);
free(buffer);
pthread_exit(NULL);
}
The 2d array memory allocation is from this question - How do I work with dynamic multi-dimensional arrays in C?.
I know I am writing everything to buffer[0][0], but that is so that I can store each in a buffer array buffer[0][1], buffer[0][2] later on based on some logic. But that shouldn't be a problem now right?
Also line is printing the correct value, whatever it is reading from stdin. So, probably strtok is the problem.
Another very similar program produces the desired output. This -
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
typedef struct { char action; int score; } Rules;
Rules rules[5] = {{'P',50}, {'L',20}, {'D',-10}, {'C',30}, {'S',40}};
int findScore(Rules* rules, char action){
int i;
for(i=0; i<5; i++){
if(rules[i].action == action)
return rules[i].score;
}
fprintf(stderr, "Action not present! Exiting...\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]){
FILE *fp;
char inputTuple[30] = {};
char buf[30] = {};
char* oB;
oB = "(";
char* cB;
char* co = ",";
cB = ")";
fp = fopen(argv[1], "r");
int score;
char *each;
size_t len = 0;
ssize_t read;
char * line = NULL;
char *eacharray;
int u = 0;
char *outputTuple;
int pad = 0;
int g;
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1) {
each = strtok(line," ,()");
while(each != NULL){
if(u%3 == 0){
outputTuple = (char *) malloc(1 + strlen(each)+ strlen(oB) );
strcpy(outputTuple, oB);
strcat(outputTuple, each);
} else if(u%3 == 1){
char q = *each;
score = findScore(rules, q);
} else if(u%3 == 2){
char * str3 = (char *) malloc(1 + strlen(outputTuple)+ strlen(co) );
strcpy(str3, outputTuple);
strcat(str3, co);
char *str4 = (char *) malloc(1 + strlen(str3)+ strlen(each) );
strcpy(str4, str3);
strcat(str4, each);
for(pad = strlen(each); pad<15; pad++)
strcat(str4," ");
sprintf(buf, "%s,%d)\n", str4, score);
printf("%s", buf);
free(outputTuple);
free(str3);
free(str4);
}
each = strtok(NULL," ,()");
u++;
}
u = 0;
}
fclose(fp);
return 0;
}
Update :
strcpy(buffer[0][0], buf); seems to be the problem. When I comment it, it is producing the output. I dont understand why is that causing a problem.

Assigning each line of a file into each element of an array

I'm quite new to C programming and have just begun studying files. I'm wondering whether it is possible to read a file line by line (including spaces in each line) into an array of size equal to the number of lines in the file. I really have no idea where to start or whether this is even possible so any guidance at all would be much appreciated.
Example
A text file in the form of:
Computer Programming
Software Engineering
Computer Architecture
to be written into array such that:
char array[4];
array[0] = "Computer Programming";
array[1] = "Software Engineering";
array[2] = "Computer Architecture";
All I have so far is:
int main()
{
char array[50];
bool answer;
FILE *classes;
classes = fopen("classnames.txt", "r");
if(classes == NULL){
printf("\n ************* ERROR *************\n");
printf("\n \"classnames.txt\" cannot be opened.\n");
printf("\n PROGRAM TERMINATED\n");
exit(EXIT_FAILURE);
}
And next I would like to write each class name into each element of the array.
Yes, you just have to declare array as char** and dynamically allocate as you read each line. E.g.
int MAX_NUM_LINES = 1000;
int MAX_LINE_LEN = 256;
char** array;
malloc(array, MAX_NUM_LINES*sizeof(char*));
fp = fopen(...);
int line_ct = 0;
char line[MAX_LINE_LEN];
while ( fgets(line, MAX_LINE_LEN, fp) != NULL )
{
int len = strlen(line);
malloc(array[line_ct], len * sizeof(char));
strcpy(array[line_ct], line);
line_ct++;
}
I have not actually tried to compile this code, but something like this will work. You can also replace MAX_NUM_LINES with the actual value by doing a quick runthrough first and counting the lines--that would be preferable probably.
This is an example of a possible approach
#include <stdio.h>
#include <string.h>
int main()
{
char array[100][100];
char line[100];
size_t arraySize;
size_t count;
FILE *file;
const char *filepath;
filepath = "<put the file path here>";
file = fopen(filepath, "r");
if (file == NULL)
{
perror("fopen()");
return -1;
}
count = 0;
arraySize = sizeof(array) / sizeof(array[0]);
while ((fgets(line, sizeof(line), file) != NULL) && (count < arraySize))
{
size_t length;
length = strlen(line);
if (line[length] == '\0')
line[--length] = '\0';
memcpy(array[count++], line, 1 + length);
}
fclose(file);
return 0;
}

Resources