C Reading words from text file - c

I'm writing a simple function that takes as input 3 already opened FILES's, then scans through each character in the file, filling up a char array until a ' ' whitespace is reached or a newline is found. However, my method of filling an array keeps giving me a Segmentation fault and I'm not really sure why. For now I am just attempting to print to console the words that were filled in the word[] array, then clearing it with memset for the next word.
hash_table_t training(FILE *D1, FILE *D2, FILE *D3, int size)
{
char *word[200];
char c;
int i = 0;
while ((c = fgetc(D1)) != EOF)
{
while (((c>='a') && (c<='z')) || ((c>='A') && (c<='Z')))
{
//add to char array
*word[i++] = c;
}
if(c == ' ' || c=='\n')
{
//hash word (print chars for now)
for (i=0; *word[i] != ' '; i++)
{
printf("%c", *word[i]);
}
}
memset (word, ' ', 20);
}
fclose(D1);
fclose(D2);
fclose(D3);
}

Your wordarray is a array of pointer, not a array of character.
You should change
char* word[200];
to
char word[200];
and
*word[i];
to
word[i];

You declared word as an array of character pointers, assign them characters, and try to dereference those values.

The variable "word" as you have declared it is not a char array/cstring, but it is a pointer to a string with a size of 200; this would work but you have not initialized the string with "malloc" hence the "Segmentation Fault" error as you are trying to change a part of memory not allocated to your program.
Edit: Also, as a tip, always initialize pointers as soon as creation; because they are pointing to random parts of memory upon creation, the code may not always give you a "Segmentation Fault" error, because it may point to a part of memory that IS allocated to your program.
Worst case, you will have a really hard-to-track bug.

Related

Manipulating and accessing char *s pointer variable (string) using another pointer variable char *c - C

I've hit a roadblock, when trying to manipulate strings when "storing" them as pointer variables.
// Sample code to input a string till line break is encountered, later re-allocating to save space
char *s = malloc(1024 * sizeof(char));
scanf("%[^\n]", s);
s = realloc(s, strlen(s) + 1);
// Code to replace whitespaces with newlines
for (char *c = s; *c != '\0'; c++) {
if (*c == ' ') {
*c = '\n';
}
}
printf("%s", s);
I want an explanation of char *c = s in the above code snippet, because when I tried to use the same concept to iterate over the string characters in another example (below) I was greeted with Segmentation fault runtime error.
I want to find the frequency of digits (0 to 9) from a string entered by the user:
int count[10];
char *s = malloc(1024 * sizeof(char));
scanf("%[^\n]", &s);
s = realloc(s, strlen(s) + 1);
for(char *c = s; *c != '\0'; c++) { // Shows Segementation fault here
if(*c >= '0' && *c <= '9') {
count[*c - '0'] += 1; // I am still trying to work this
}
}
In scanf you are using &s instead of s.
explanation of char *c = s:
in this statement, the pointer c is set to the same address as the pointer s for the first iteration of the for-loop. That means, c and s point to the same memory location. Then, for each further iteration of the for-loop, c is incremented (c++) and the for loop is executed as long as the dereferenced memory location (the content at the memory location) is not the null character.
Now for the segmentation fault, as you didn't post the non-working code, I can only guess. One guess could be that there is no null character inside the allocated memory, so the for loop is not stopping soon enough and increments the c pointer to an address higher than allocated with malloc. By dereferencing it you access a memory location outside the allocated memory. This can produce a segmentation fault.
Edit:
ah, now I see you added the non-working code. Yes, Akshay's answer is right and the segmentation fault could already happen there, as you don't scanf to s but to the memory location where the pointer variable is saved. The array pointed by s is still uninitialized (unknown values) but you search for the first occurence of the null character with strlen. Now, realloc may fail and thus return NULL and then you would set your c pointer to NULL. That would also produce a segmentation fault.

How to read part of string into smaller string

I am reading a line from a .txt file. My goal right now is to read the first word of the line. I have the line stored in a string declared char *text. Then I have a double pointer to that string char** charReader which will read the letters of the string. I am trying to read the characters in one by one into a new string char* word, and stop when I hit a space, however I have gotten a segmentation fault in doing so.
Here is my code to perform that function:
while(isalpha(**charReader) != 0){
word[i] = **charReader;
charReader++;
i++;
}
My goal is to output the value of word. But my code seems to be giving me a segmentation fault.
The variable charReader is a pointer to a pointer to char (you can think of it as being an array of strings or a matrix of chars), while the variable word is a string (an array of chars). Therefore, when you do:
word[i] = **charReader;
you are trying to make an assignment involving incompatible types (treating a double pointer to char as if it was a char). That's why you are getting an error.
To copy the first word of a string into another string, you can try something like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char text[] = "This is a string with multiple words.";
int i = 0;
while(i++ < strlen(text) && text[i] != ' '); //seek for the index of the first space character in "text"
char *word = malloc((i + 1) * sizeof(char));
strncpy(word, text, i); //"word" now stores the first word of "text"
printf("%s\n", word);
free(word);
return 0;
}

Copy a string from a pointer to an index within an array of pointers

As stated in the title, I wanted to copy a string from a char pointer to location within an array of char pointers. When doing strcpy(), the output results in seg fault, but don't understand why this occurs.
The abbreviated code has the following:
void make_history(char *entry) {
//static char past_entries[10][10];
static char *past_entries[10];
static int index = 0;
static int array_index = 0;
char *input;
if((input = strchr(entry, '\n')) != NULL)
*input = '\0';
if(strcmp(entry, "history") != 0) {
strcpy(past_entries[index], entry);
*past_entries[index] = &entry;
index = (index + 1) % 10;
array_index++;
}
}
Instead of trying to return a 2d array (which is also very tricky), I thought it would be easier to copy the date from entry to the location within the array of pointers past_entries. Again, strcpy does not work, is there a valid reason as to why this occurs, and a possible workaround or solution to this fix?
Thank you
In your example past_entries is just an array of pointers, but you don't allocate any memory to them (initially pointing to NULL). You then try to write to those locations, hence the crash.
To solve this, just allocate some memory before you try to copy your string to it:
past_entries[index] = malloc(strlen(entry) + 1);
Of course you should not forget to free all of this in the end.
Oh and, remove that line: *past_entries[index] = &entry;. It tries to assign a pointer to a character array to a character.

Need to debug segmentation fault (core dumped) in C

Below is the function that takes a character array allowing max 10 characters and checks if its lesser than 9999999999.0. I am getting "segmentation fault(core dumped) error during run time. Please someone help me debug this function.
bool validate(char* token)
{
int i=0, count=0;
char* temp = 0;
while(token[i] != '\0' && count < 10)
{
temp[i] = token[i];
i++;
count++;
}
temp[i] = '\0';
float check = strtof(temp,NULL);
if (check > 9999999999.0)
return false;
return true;
}
Allocate memory for temp. Because it can have at max 10 characters, you need to allocate memory for 11 bytes (one is for the '\0' character)
If you're sure that you'll always have at max 10 characters then allocate it statically:
char temp[11];
If that's not the case then you might want to allocate the memory dynamically.
int noOfCharacters = // value
char* temp = malloc(noOfCharacters + 1);
I can't figure out what you're actually trying to do here but...
You are overflowing your buffer. Your assigned char* temp = 0; is completely incorrect and probably the cause of the overflow. Remember that in C, strings are IMMUTABLE. Instead, try initializing char temp[50]; where 50 is replaced by whatever number is large enough to hold your entire end result. IE- make an array of characters large enough to contain whatever it is you're trying to contain.
warning: Remember that in C, every character takes 1 byte of memory and so a very large data set will need to be approached differently.
The best way is to here is to pass the size of the token as an argument to the validate function. In that manner, you can allocate memory to your temp pointer at run time
example
...
bool validate(char* token, int size)
{
int i=0, count=0;
char* temp = 0;
char* temp = (char*) malloc(size + 1);
//continue here
...
}

Structures and Pointers in C - crash when using strcpy

I have an assignment that is supposed to be written in C (not C++), in which I need to create some structs from reading multiple text files. I have learnt c before (2 years ago) - I'm far more comfortable with Java, just can't use that for this project. I guess my issue comes from not understanding the pointer syntax very well :/.
However, my real issue:
The code I have written crashes when I try to use the strcpy function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
char* filename;
int time;
} JOB;
JOB **jobQueue;
int nJobs;
void trimLine(char* line) {
for (int i = strlen(line); i >=0; i--) {
if (line[i] == '\n' || line[i] == '\r') line[i] = '\0';
}
}
int main(int argc, char* argv[]) {
if (argc !=2) {
printf("Error - Usage is: my_project file\n");
exit(-1);
}
FILE *fp;
fp = fopen(argv[1],"r");
if (fp==NULL) {
printf("Error - file %s could not be read.\n",argv[1]);
exit(-1);
}
jobQueue = malloc(3*sizeof(JOB*));
char filename[BUFSIZ];
nJobs = 0;
while (fgets(filename,sizeof(jobfilename),fp)!=NULL) {
trimLine(filename);
JOB* newjob;
newjob = malloc(sizeof(JOB));
//** THIS IS WHERE IT SCREWS UP
strcpy(newjob->filename,filename);
jobQueue[nJobs++] = newjob;
}
}
If I delete the line containing strcpy, the program runs fine (well, I realise this part doesn't really do anything, but still). However, when the program contains the strcpy line, it breaks when attempting to do Job #2. Any idea why?
Also: If I need to maintain an array of JOBs for use in other functions, is the way I have done it correct? JOB **jobQueue is an array of pointers to JOBs, JOB *newjob is a pointer to a JOB, would this work correctly?
newjob->filename is a wild pointer(not set to anything), you have to allocate memory before you can store things at it.
Change:
typedef struct{
char* filename;
int time;
} JOB;
to:
#include <limits.h>
typedef struct{
char filename[PATH_MAX];
int time;
} JOB;
I'd like to add a few suggestions
nJobs = 0;
Globals are initialised with 0, you don't need to do it manually.
while (fgets(filename,sizeof(jobfilename),fp)!=NULL) {
jobfilename is not declared in your code. I guess you mean filename.
for (int i = strlen(line); i >=0; i--) {
if (line[i] == '\n' || line[i] == '\r') line[i] = '\0';
}
You start with the ending \0 which you could skip.
You declare new variables everywhere you like, it's good practice (and C89 standard) that increases readability to declare variables at the start of a code block.
Additional suggestions for improvement of your code:
You do never free() the malloced pointers.
What is there are more than 3 Jobs?
Your code doesn't handle this. You
could use a linked list instead of an
array.
You do not call fclose() on your file handle.
Trasvi, don't forget that your jobQueue is malloc'ed to hold only 3 instances of the JOB struct. However, your "while loop" goes around as many times as the user inputs.
But to answer your original question, simply add this to your code before the strcpy.
newjob->filename = malloc ( strlen( filename) +1 );
//You only need to malloc the amount of characters in the filename + 1,
//which is for the null-terminated char, and you don't need to worry about
//multiplying by 'sizeof' because a char is one byte on any compiler.
You have a null pointer in newjob->filename:
int nJobsMax=3;
char* filename;
JOB* newjob;
...
jobQueue = malloc(nJobsMax*sizeof(JOB*));
filename=(char*)malloc(BUFSIZ);
while (fgets(filename,BUFSIZ,fp)!=NULL) {
trimLine(filename);
newjob = (JOB*)malloc(sizeof(JOB));
newjob->filename = filename;
filename=(char*)malloc(BUFSIZ);
jobQueue[nJobs++] = newjob;
if (nJobs > nJobsMax)
//possible buffer overflow need escape
}
free(filename);
fclose(fp);
more things:
void trimLine(char* line) {
int i = strlen(line)-1;
do{
if (line[i] == '\n' || line[i] == '\r')
line[i] = '\0';
}while(!(line[i]>=' ')||i-->=0);
}
really you don't need iterate all the string
example: fgetd output => text_text_text\r\n\0aq
' ' is character space values over this element are printer characters see ascii.
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte (aq\0aq) is stored after the last character in the buffer.
source: fgets
strncpy is more recommended that strcpy because protect your code against buffer overflow.
The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated.
If the length of src is less than n, strncpy() writes additional null bytes to dest to ensure that a total of n bytes are written.
source:strncpy
other solution to strcmp:
The strdup() function returns a pointer to a new string which is a duplicate of the string s. Memory for the new string is obtained with malloc(3), and can be freed with free(3).
The strndup() function is similar, but only copies at most n bytes. If s is longer than n, only n bytes are copied, and a terminating null byte ('\0') is added.
source:strndup

Resources