Segmentation faul even with memory allocation of 2d array - c

I'm trying to read a file that contains a five letter word on each line for 12972 lines. I'm not sure why I'm getting this error even with freeing storage.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *file;
file = fopen("words.txt", "r"); // Opens file
char** gssArr = (char**)malloc(12972 * sizeof(char*)); // Allocates memory for 2d array
for(int i = 0; i < 12972; i++)
{
gssArr[i] = (char*)malloc(5 * sizeof(char));
}
char word[6]; // Current word being looked at from file
int current = 0; // Used for indexing x coordinate of 2d array
while(fgets(word,6,file) != NULL) // Not at end of file
{
if(word[0] != '\n') // Not at end of line
{
for(int j = 0; j < 5; j++) // Loops through all 5 letters in word, adding them to gssArr
{
gssArr[current][j] = word[j];
}
}
current++; // Index increase by 1
}
fclose(file); // Close file, free memory
free(gssArr);

FYI - your reader loop - you may want to make sure that your current index is not going beyond 12971.

Your problem is here:
current++;
Why?
Because it's done even when you are at the end of the line.
One solution is to move it inside the if statement but instead I'll recommend that you use fgets with a much larger buffer.
Details
If every line holds a five letter word then
fgets(word,6,file)
will read the five letters and zero terminate it. Then the next fgets will just read the "newline". Still you increment the index counter and in the end, you write outside allocated memory.
Try
while(fgets(word,6,file) != NULL) // Not at end of file
{
printf("current=%d\n", current);
and you see the problem.

Related

C / Getting line string from text file and storing them in array/pointer

I want to get all lines from the text file and store them in my char** pointer (array of strings). The problem is that when I try to set indices for pointer's strings, the program assigns the last scanned sentence for all indices.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE 10000
int main()
{
FILE *fp = NULL;
char line[MAX_LINE];
char** lines = (char**) malloc(10000*200*sizeof(char));
int count = 0;
fp = fopen("test.txt","r");
while(fgets(line,10000,fp)) {
lines[count] = line;
count++;
}
fclose(fp);
for(int i =0; i<2000;i++){
printf("%s",lines[i]);
}
return 0;
}
lets assume test.txt is like this:
Alice was beginning to get very tired of sitting by her sister on the
bank, and of having nothing to do: once or twice she had peeped into the
book her sister was reading, but it had no pictures or conversations in
it, and what is the use of a book, thought Alice without pictures or
conversations?
When I print like this, every time I get the last sentence (in this case conversations? ) in my text file. However, I want to set every scanned sentence from the text file to the different index in my char**. For example, I want to set like this:
lines[0] gives "Alice was beginning to get very tired of sitting by her sister on the"
lines[1] gives "bank, and of having nothing to do: once or twice she had peeped into the"
and so on.
You can't copy characters from one string buffer to another simply by assigning a pointer (all that does is to make the destination point to the source, as you have noticed).
Instead, you must actually copy the characters, using the strcpy function. So, instead of:
lines[count] = line; // Just makes each pointer point to the same buffer
use:
strcpy(lines[count], line); // Copies the CURRENT contents of "line"
You also have a severe problem in the way you are using your char** lines buffer. If you want an array of 200 lines, each with a maximum length of 10000 characters, you should allocate them as follows:
char** lines = malloc(200 * sizeof(char*)); // Make 200 pointers
// Now allocate 10000 chars to each of these pointers:
for (int i = 0; i < 200; ++i) lines[i] = malloc(10000 * sizeof(char));
Note: The 200 buffers will be uninitialized (contain random data) so, in your print loop, you should only use those you have copied real data to, using the count variable as the loop limit:
for(int i = 0; i < count; i++) {
printf("%s", lines[i]);
}
Also, don't forget to free the memory allocated when you're done:
for (int i = 0; i < 200; ++i) free(lines[i]); // Free each line buffer...
free(lines); // ... then free the array of pointers itself
strdup resolve the issue, free resources as said by Adrian when finished.
int main()
{
FILE *fp = NULL;
char line[MAX_LINE];
char** lines = (char**) malloc(10000*200*sizeof(char));
int count = 0;
fp = fopen("test.txt","r");
while(fgets(line,10000,fp)) {
lines[count] = strdup(line);
count++;
}
fclose(fp);
for(int i =0; i<count;i++){
printf("%s",lines[i]);
}
for (int i = 0; i < count; ++i) free(lines[i]);
free(lines);
return 0;
}
If you are looking for better performance look at my repo (https://github.com/PatrizioColomba/strvect)

Can't scan in .txt file using fgets into flexible data structure

I have a homework task that requires me to process .txt files by scanning them into a flexible data structure and then searching the files for words with capital letters. I'm having issues scanning them in this flexible data structure I'm using. The reason that the data structure needs to be flexible is that it needs to be able to process any .txt files.
The data structure I want to use is an array that points to arrays that contains the content of the line. I'm open to using a different structure if it's easier.
I've tried to scan it in line by line using fgets, and using malloc to allocate just enough to store the line, but it doesn't seem to work.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STEPSIZE 100
int main()
{
FILE *inputFile;
//Opens the file in read mode
inputFile = fopen("testfile.txt", "r");
//Error message if file cannot open
if (inputFile == NULL)
{
printf("Unable to open file");
return 1;
}
int arrayLen = STEPSIZE;
// Allocate space for 100 lines. The **lines is the data structure used to store all the lines
char **lines = (char **)malloc(STEPSIZE * sizeof(char*));
char buffer[3000];
int i = 0;
while (fgets(buffer, 3000, inputFile))
{
//Checks if the array is full, and extends it
if(i == arrayLen)
{
arrayLen += arrayLen;
char ** newLines = realloc(lines, 200 * sizeof(char*));
if(!newLines)
{
printf("cant realloc\n");
}
lines= newLines;
}
// Get length of buffer
int lengthOfBuffer = strlen(buffer);
//Allocate space for string. The +1 is for the terminating character
char *string = (char *)malloc((lengthOfBuffer + 1) * sizeof(char));
//copy string from buffer to string
strcpy(string, buffer);
//Attach string to data structure
lines[i] = string;
//Increment counter
i++;
printf("%s", lines[i]);
}
//Closes the file
fclose(inputFile);
for (int j = 0; j < 100; j++){
printf("%s \n", lines[i]);
}
return 0;
}
When the final for loop runs, ideally the contents of the file gets printed, just to show that it has been stored and is able to be processed, but currently i get exit code 11.
Any help would be appreciated.
There is a problem is here:
//Increment counter
i++;
printf("%s", lines[i]); // you're printing the next file that does not yet exist
Correct code:
printf("%s", lines[i]);
//Increment counter
i++;
And another one here:
for (int j = 0; j < 100; j++) { // your loop variable is j
printf("%s \n", lines[i]); // but you use i here.
}
Correct code:
for (int i = 0; i < 100; i++) {
printf("%s \n", lines[i]);
}
And still another one here:
arrayLen += arrayLen;
char ** newLines = (char**)realloc(lines, 200 * sizeof(char*));
// here the new length of your array is inconditionally 200
// but actually the new array length is arrayLen
Correct code:
arrayLen += arrayLen;
char ** newLines = (char**)realloc(lines, arrayLen * sizeof(char*));
There may be more problems though, I didn't check everything.
BTW: sizeof(char) is 1 by definition, so you can just drop it.
BTW2: arrayLen += arrayLen; are you sure this is what you want? You double the size of your array each time. This is not necessarily wrong but using this method the array length will very quickly grow to a very big number. You probably wanted this: arrayLen += STEPSIZE;
BTW3:
while (fgets(buffer, 3000, inputFile))
this is not actually wrong, but you'd better write this:
while (fgets(buffer, sizeof buffer, inputFile))
which eliminates one of the two hard coded constants 3000.
BTW4: at the end you only print the first 100 lines yo've read. You should be able to correct this yorself.
BTW5: you should also free all the memory you have allocated. I leave this as an exercise to you. Hint: it's about three lines of code to add at the end of main.

fscanf in C with a text file with no spaces

I have a text file with names that looks as follows:
"MARY","PATRICIA","LINDA","BARBARA","ELIZABETH","JENNIFER","MARIA","SUSAN","MARGARET",
I have used the following code to attempt to put the names into an array:
char * names[9];
int i = 0;
FILE * fp = fopen("names.txt", "r");
for (i=0; i < 9; i++) {
fscanf(fp, "\"%s\",", names[i]);
}
The program comes up with a segmentation fault when I try to run it. I have debugged carefully, and I notice that the fault comes when I try and read in the second name.
Does anybody know why my code isn't working, and also why the segmentation fault is happening?
You have undefined behavior in your code, because you don't allocate memory for the pointers you write to in the fscanf call.
You have an array of nine uninitialized pointers, and as they are part of a local variable they have an indeterminate value, i.e. they will point to seemingly random locations. Writing to random locations in memory (which is what will happen when you call fscanf) will do bad things.
The simplest way to solve the problem is to use an array of arrays, like e.g.
char names[9][20];
This will gives you an array of nine arrays, each sub-array being 20 characters (which allows you to have names up to 19 characters long).
To not write out of bounds, you should also modify your call so that you don't read to many characters:
fscanf(fp, "\"%19s\",", names[i]);
There is however another problem with your use of the fscanf function, and that is that the format to read a string, "%s", reads until it finds a whitespace in the input (or until the limit is reached, if a field width is provided).
In short: You can't use fscanf to read your input.
Instead I suggest you read the whole line into memory at once, using fgets, and then split the string on the comma using e.g. strtok.
One way of handling arbitrarily long lines as input from a file (pseudoish-code):
#define SIZE 256
size_t current_size = SIZE;
char *buffer = malloc(current_size);
buffer[0] = '\0'; // Terminator at first character, makes the string empty
for (;;)
{
// Read into temporary buffer
char temp[SIZE];
fgets(temp, sizeof(temp), file_pointer);
// Append to actual buffer
strcat(buffer, temp);
// If last character is a newline (which `fgets` always append
// if it reaches the end of the line) then the whole line have
// been read and we are done
if (last_character_is_newline(buffer))
break;
// Still more data to read from the line
// Allocate a larger buffer
current_size += SIZE;
buffer = realloc(buffer, current_size);
// Continues the loop to try and read the next part of the line
}
// After the loop the pointer `buffer` points to memory containing the whole line
[Note: The above code snippet doesn't contain any error handling.]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *names[9], buff[32];
int i = 0;
FILE *fp = fopen("names.txt", "r");
for(i = 0; i < 9; i++) {
if(1==fscanf(fp, "\"%31[^\"]\",", buff)){//"\"%s\"," does not work like that what you want
size_t len = strlen(buff) + 1;
names[i] = malloc(len);//Space is required to load the strings of each
memcpy(names[i], buff, len);
}
}
fclose(fp);
//check print & deallocate
for(i = 0; i< 9; ++i){
puts(names[i]);
free(names[i]);
}
return 0;
}
try this...
for (i=0; i < 9; i++)
{
names[i]=malloc(15);// you should take care about size
fscanf(fp, "\"%s\",", names[i]);
}

Possible memory corruption in 2-D array that stores the contents of a file

I've been able to work most of the problems out, but I've encountered a few that I'm uncertain as to how to address.
Let me explain: I have an example file that contains all the non-distinct combinations of the lower case alphabet up to length two (i.e. aa, ab, ac, ad...). The total number of non-distinct combinations is therefore 26^2, 676.
Knowing this is quite useful because I know that there are 676 lines, each containing a string of length 2.
Here is the code:
#include <stdlib.h>
#include <string.h>
int main(){
FILE* file;
//These would be passed as function arguments
int lines = 676;
int sLength = 2;
int C = lines+1;
int R = sLength+2;
int i,j; //Dummy indices
int len;
//Create 2-D array on the heap
char** mystring = (char**) malloc(C*sizeof(char*));
for(i = 0; i<C; i++) mystring[i] = (char *) malloc(R*sizeof(char)); //Need to free each element individually later on
//Create temporary char array
char line[R];
//Open file to read and store values
file = fopen("alphaLow2.txt", "r");
if(file == NULL) perror("Error opening file");
else{
i = 0;
while(fgets(line, sizeof(line), file) != NULL){
//Remove next line
len = strlen(line);
if((line[len-1]) == '\n') (line[len-1]) = '\0';
len--; //Decrement length by one because of replacing EOL with null terminator
//Copy character set
strcpy(mystring[i], line); //NOT certain if this works being that mystring is 2-D
i++;
}
mystring[C] = '\0'; //Append null terminator
}
for(i = 0; mystring[i]; i++){
for(j = 0; mystring[i][j]; j++){
printf("%c", mystring[i][j]);
}
}
getchar();
return 0;
}
To explain further, int C = lines+1 in order to append a null terminator. int R = sLength+2 in order to account for the \n present in the file, and the fact that fgets() always adds a null terminator. Therefore, in this example, the array should be of dimensions mystring[C][R] --> mystring[677][4]. The while loop replaces the \n with \0. The printout is correct, but at the very end of the printout, there is an odd string:
The characters after zz should not be there. This leaves me to wonder whether there is some memory corruption occurring somewhere in my program. Perhaps my arrays aren't large enough? I'm not sure. Otherwise, everything else appears to be functioning correctly.
Also, I will free the memory in the final program. Right now, I'm trying to get this to work properly. I'm also aware there are much easier ways to do this. For instance, std::string array would make this much easier. However, I'm am copying this array to a cuda enabled device, and cuda does not recognize that form.
Any constructive input is appreciated.
mystring is a char** so this call mystring[C] = '\0' is not quite correct. mystring[C] is a char* so you should not assign a char value to it. In this case the code works because \0 is 0 and NULL is also 0. What you can do is to replace this call with mystring[C-1] = NULL.
Also you assign NULL to the last row of mystring but it is not guaranteed that the file always has C - 1 lines(so that if you assign NULL to mystring[C-1] you are certain all the previous lines have been read). Better add a counter in the cycle reading lines and assign NULL to the next row in mystring.
As noted in a comment above mystring[C] is out of bounds for mystring so you should replace the index C with C - 1.
You don't have a terminating NULL entry in your mystring array, so your
for(i = 0; mystring[i]; i++)
will end up reading a bit too far. That may be the cause of your troubles.

Need to reverse file in place but it only works for one line files

So i think im closer here but im still getting funny results when printing the reversed string in place. I'll try to be detailed.
Here is the input:
Writing code in c
is fun
Here is what i want:
c in code Writing
fun is
Here is the actual output:
C
in code Writing
fun
is
Here is my code:
char str[1000]; /*making array large. I picked 1000 beacuse it'll never be written over. A line will never hole 1000 characters so fgets won't write into memory where it doesnt belong*/
int reverse(int pos)
{
int strl = strlen(str)-1,i;
int substrstart = 0,substrend = 0;
char temp;
for(;;)
{
if( pos <= strl/2){ /*This will allow the for loop to iterate to the middle of the string. Once the middle is reached you no longer need to swap*/
temp = str[pos]; /*Classic swap algorithm where you move the value of the first into a temp variable*/
str[pos]= str[strl-pos]; /*Move the value of last index into the first*/
str[strl-pos] = temp; /*move the value of the first into the last*/
}
else
break;
pos++; /*Increment your position so that you are now swaping the next two indicies inside the last two*/
} /* If you just swapped index 5 with 0 now you're swapping index 4 with 1*/
for(;substrend-1 <= strl;)
{
if(str[substrend] == ' ' || str[substrend] == '\0' ) /*in this second part of reverse we take the now completely reversed*/
{
for(i = 0; i <= ((substrend-1) - substrstart)/2; i++) /*Once we find a word delimiter we go into the word and apply the same swap algorthim*/
{
temp = str[substrstart+i]; /*This time we are only swapping the characters in the word so it looks as if the string was reversed in place*/
str[substrstart+i] = str[(substrend-1)-i];
str[(substrend-1)-i] = temp;
}
if(str[substrend] == '\t' || str[substrend] == '\n')
{
str[substrend] = ' ';
for(i = 0; i <= ((substrend-1) - substrstart)/2; i++) /*Once we find a word delimiter we go into the word and apply the same swap algorthim*/
{
temp = str[substrstart+i]; /*This time we are only swapping the characters in the word so it looks as if the string was reversed in place*/
str[substrstart+i] = str[(substrend-1)-i];
str[(substrend-1)-i] = temp;
}
}
if(str[substrend] == '\0')
{
break;
}
substrstart=substrend+1;
}
substrend++; /*Keep increasing the substrend until we hit a word delimiter*/
}
printf("%s\n", str); /*Print the reversed line and then jump down a line*/
return 0;
}
int main(int argc, char *argv[])
{
char *filename; /*creating a pointer to a filename*/
FILE *file20; /*creating FIlE pointer to a file to open*/
int n;
int i;
if (argc==1) /*If there is no line parameter*/
{
printf("Please use line parameter!\n");
return(5); /*a return of 5 should mean that now line parameter was given*/
}
if(argc>1){
for(i=1; i < argc; i++)
{
filename = argv[i]; //get first line parameter
file20 = fopen(filename, "r"); //read text file, use rb for binary
if (file20 == NULL){
printf("Cannot open empty file!\n");
}
while(fgets(str, 1000, file20) != NULL) {
reverse(0);
}
fclose(file20);
}
return(0); /*return a value of 0 if all the line parameters were opened reveresed and closed successfully*/
}
}
Can anyone point me to an error in the logic of my reverse function?
What you've written reads out the whole file into a single buffer and runs your reverse function over the whole file at once.
If you want the first line reversed then the next line reversed, etc, you'll need to read the lines one at a time using something like fgets. Run reverse over each line, one at a time and you should get what you want.
http://www.cplusplus.com/reference/cstdio/fgets/
Assuming you want to continue reading in the whole file into a single buffer and then doing the line-by-line reverse on the buffer all at once (instead of reading in one line, reversing it, reading in the next line, reversing it, and so on), you'll need to re-write your reverse() algorithm.
What you have in place seems to work already; I think you can get what you need by adding another loop around your existing logic, with a few modifications to your existing logic. Start with a pointer to the beginning of str[], let's call it char* cp1 = str. At the top of this new loop, create another pointer, char* cp2, and set it equal to cp1. Using cp2, scan to the end of the current line looking for a newline or '\0'. Now you have a pointer to the start of the current line (cp1) and a pointer to the end of the current line (cp2). Now modify your existing logic to use those pointers instead of str[] directly. You can compute the length of the current line by simply lineLen = cp2 - cp1; (you wouldn't want to use strlen() because the line might not have a terminating '\0'). After that, it will loop back up to the top of your new loop and continue with the next line (if *cp2 doesn't point to '\0')... just set cp1 = cp2+1 and continue with the next line.

Resources