I'm trying to use malloc with a function I wrote in order to grow a list of unique states(no duplicates)
My file contains strings such as;
Kmart, 295 Hartford Turnpike, Vernon CT
The function I wrote extracts the states("CT") from a file;
#define MAX_CHARS_PER_LINE 80
void getState(char strState[], const char strLine[])
{
char newLine[MAX_CHARS_PER_LINE+1];
char newState[MAX_CHARS_PER_LINE+1];
strcpy(newLine, strLine);
char* token = strtok(newLine, ",");
if(token != NULL)
{
token = strtok(NULL,",");
token = strtok(NULL, ",");
}
strcpy(newState, token);
unsigned long length = strlen(newState)-5;
strcpy(strState, newState+length);
}
This is my main function which I am trying to find the unique list of states using strcmp and grow it using malloc;
int main(void)
{
char **states[3];
char buffer[MAX_CHARS_PER_LINE+1];
FILE* fptr;
fptr = fopen(fileName, "r");
if(fptr == NULL)
{
exit(EXIT_FAILURE);
}
else
{
while(fgets(buffer, sizeof(buffer), fptr))
{
getState(states, buffer);
}
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
if(strcmp(states[i], states[j]))
{
states[i] = malloc(3* sizeof(states));
}
}
}
fclose(fptr);
free(states);
}
return EXIT_SUCCESS;
}
I'm a bit confused on how to correctly use malloc and strcmp to get this unique list. My get state function works fine, it's just my main I have problems with
A few things:
a. In the getState function, change the length variable from strlen(newState)-5 to strlen(newState)-2. States are only 2 letters, and strlen doesn't count the terminating character.
b. Don't use a triple pointer for "states"; use a double pointer. It should just be a string of strings.
c. Use the iterator for the states list for getState. MAKE SURE TO RE-MALLOC TO INCREASE THE SIZE AND COPY THE OLD STATES WITH A BACKUP POINTER BEFORE CALLING GETSTATES
d. Iterate through the states array with getState.
e. Make either a second array for the second iteration to copy each unique state name, OR just make a new string variable, use getState on that, then iterate through states with strcmp, and if there's no matches, THEN add that state to the states array.
Related
I'm writing a C code that checks the number of occurrences of a word (entered by the user) in a text file, prints the count per line, and the total count on the screen, and compares the word with the last word of the file.
I have dedicated a function to fetch the last word called "Fetch", that fetches the word and returns it to the main function. then another function counts the occurrences, then a third function that actually compares the two strings using strcmp().
my problem is that the function char* Fetch() is seemingly returning empty strings for some reason. Note that I wanted to check where the problem actually is, so I tried to print the result inside the function on the screen instead of returning it to main() and it worked!!, so seemingly, the problem is the return statement, What could be the problem??
char* Fetch() called in main():
//step 3: fetch the last word in the file
Lword = Fetch();
printf("the last word is %s", Lword);
char* Fetch():
char* Fetch()
{
char text[1000];
fread(text, sizeof(char), sizeof(text), spIn);
for(int i = 0; i < strlen(text); i++)
{
if(isspace(text[strlen(text) -1 -i])) //if space
{
return (text + (strlen(text)-i)); //return a pointer to the element after the space
}
}
}
declarations in main:
char Uword[20], *Lword;
int TotalCount;
You allocated a local char buffer text in function Fetch and store data into.
When function Fetch returns, the buffer text will be released, then the content should NOT be used anymore.
Function Fetch can be changed this way:
char* Fetch(char *text, size_t maxlen, FILE *spIn)
{
fread(text, sizeof(char), maxlen, spIn);
for(int i = 0; i < strlen(text); i++)
{
if(isspace(text[strlen(text) -1 -i])) //if space
{
return (text + (strlen(text)-i)); //return a pointer to the element after the space
}
}
return text;
}
And can be called like this:
FILE *spIn = ...
char text[1000] = {'\0'};
char *res = Fetch(text, sizeof(text), spIn);
printf("the last word is: %s\n",res);
Remember to use the text buffer when it's still valid.
I am currently trying to understand how to go through a .txt file in C and I think I have mostly everything worked out but what I need to do is kind of confusing. I need to create an array of Pointers to point to structs.
Each line in my .txt file should have information corresponding to a single struct. Each line should start with a name followed by some float values.
My question is, when I read the lines and parse them using strtok first, how would I get that information in a struct?
second how would I then make the sample pointer at index i point to the struct?
I tried doing the name seperate from the numbers since the numbers need their own special atof conversion since initially it will be a string. However I think this is probably incorrect since I want to read multiple lines, the code I have before the while loop for obtaining the name will only run once so any following lines will not have the name seperated. I can technically delimit my text file as I choose, so maybe I can just seperate the name with a semicolon and the rest spaces?
If this question seems confusing its probably because I am over thinking
Should I be declaring a struct such as : Sample tmp;
I've been reading examples but I can't figure out how to put the information together. Let me know if I declared my array of pointers incorrectly... Which I think I did. I think my the line that says:
sample arr[SIZE] = {NULL}; might be incorrect but I am not sure. if you can help me work out the logic behind all this I would appreciate it. Thanks.
typedef struct sample{
char* name;
int list_len;
float* value_list;
}sample;
void read_and_parse(){
const int SIZE = 1024;
sample* sample = (sample*)malloc(sizeof(sample); //pointer allocation?
FILE* fin;
fin = fopen("record.txt", "r");
if (fin == NULL) {
printf("record.txt could not be opened \n");
exit(1);
}
else {
int i= 0;
sample arr[SIZE] = {NULL}; //here I try to make the array of pointers
char linebuf[SIZE];
token = strtok(linebuf, " "); //grab the first item
while (fgets(linebuf, SIZE, fin) && i<SIZE) {
arr[i] = malloc(sizeof(sample));
arr[i.name] = token;
token = strtok(NULL, " ");
// now parse the linebuf and fill arr[i] with it
i++;
}
Edited: 11/02/2017
any print statements you see are just silly markers I placed for testing and recognizing what is running when I finally get this code compiled
Here is a much better edited version of the code. I think it should work now.
typedef struct sample{
char* name;
int list_len;
float* value_list;
}sample;
void read_and_parse(FILE **fin, sample* arr[]){
const int SIZE = 1024;
if (*fin == NULL) {
printf("record.txt could not be opened \n");
exit(1);
}
else {
printf("successfully opened file\n");
char linebuf[SIZE];
while ( fgets(linebuf, SIZE, fin) ) {
arr[i] = malloc(sizeof(sample));
int floats_per_line = 0;
while(linebuf[i]){
if(linebuf[i] == ' ');
++floats_per_line;
}
arr[i]->list_len = values_per_line;
arr[i]->value_list = (float*)malloc(sizeof(float)*floats_per_line);
arr[i]->name = strdup(strtok(linebuf, ' '));
char* tok;
int j = 0
while(tok = strtok(NULL, ' ')){
arr[i]->value_list[j] = atof(tok);
++j
}
i++;
}
}
fclose(fin);
}
How would I read a line, parse the information and then attribute it to a struct ?
Read with fgets() which converts a line of file input into a string. OP does that well. Then parse the string.
when I read the lines and parse them using strtok first, how (to) get that information in a struct?
Should I be declaring a struct such as : sample tmp;
Pass the string to a helper function to parse it into a sample that can hold any input. So the pointer members of tmp need to point to maximal space.
char name[SIZE];
char f[SIZE/2];
sample tmp = { name, 0, f };
while (i<SIZE && fgets(linebuf, SIZE, fin)) {
if (sample_parse(&tmp, linebuf) == NULL) {
break; // Parsing failed for some reason, perhaps an error message?
}
// Now populate arr[i] with right-sized memory allocations
arr[i].name = strdup(tmp.name); // ToDo: add NULL check
arr[i].list_len = tmp.list_len;
size_t f_size = sizeof *(tmp.value_list) * tmp.list_len;
arr[i].value_list = malloc(f_size); // ToDo: add NULL check
memcpy(arr[i].value_list, tmp.value_list, f_size);
i++;
}
so maybe I can just separate the name with a semicolon and the rest spaces?
Yes. Also allow other white-spaces too.
if I declared my array of pointers incorrectly.
Code does not have an array of pointers anywhere.
Recommend using size_t for array size type.
typedef struct sample {
char* name;
// int list_len;
size_t list_len;
float* value_list;
} sample;
Some untested code for parsing. Parse the line with strtok(). Further parse the number tokens with strtof().
#define sample_NAME_DELIMITER ":"
#define sample_NUMBER_DELIMITER " \n\t\r"
// parse for a name and then 0 or more numbers
static sample *sample_parse(sample *dest, char *linebuf) {
char *s = strtok(linebuf, sample_NAME_DELIMITER);
if (s == NULL) {
return NULL; // no name - TBD on if this is allowed
}
strcpy(dest->name, s);
size_t i = 0;
while ((s = strtok(NULL, sample_NUMBER_DELIMITER)) != NULL) {
char *endptr;
dest->value_list[i] = strtof(s, &endptr);
if (s == endptr || *endptr) {
// conversion failed or extra junk
break;
}
i++;
}
dest->list_len = i;
return dest;
}
How can I create an array of unique strings without knowing how many strings there are until I process the input file? There can be as many as 2 million strings, max length of 50.
My program is something like this. This works for 51 items then overwrites other data. I don't know how to add an element to the array, if possible.
main() {
char *DB_NAMES[51]; // i thought this gave me ptrs to chunks of 51
// but it's 51 pointers!
char *word;
while not eof {
...function to read big string
...function to separate big sting into words
...
processWord(ctr, DB_NAMES, word);
...
}
}
processWord(int ndx, char *array1[], char *word){
...function to find if word already exists...
//if word is new, store in array
array1[ndx]= (char *)malloc(sizeof(51)); // isn't this giving me a char[51]?
strcpy(array1[ndx],word);
...
}
You can first get the number of words in your file using the below logic and when you get the number of words in the file you can initialize the array size with the word count.
#include<stdio.h>
#define FILE_READ "file.txt"
int main()
{
FILE * filp;
int count = 1;
char c;
filp = fopen(FILE_READ, "r");
if(filp == NULL)
printf("file not found\n");
while((c = fgetc(filp)) != EOF) {
if(c == ' ')
count++;
}
printf("worrds = %d\n", count);
return 0;
}
Regards,
yanivx
Better not use a fixed string length; save memory space.
char **DB_NAMES = 0; // pointer to first char * ("string") in array; initially 0
Pass pointer by reference so that it can be altered. Moreover, you'll want the new ctr value in case a new word has been stored.
ctr = processWord(ctr, &DB_NAMES, word);
Change function processWord accordingly.
int processWord(int ndx, char ***array1a, char *word)
{ char **array1 = *array1a;
...function to find if word already exists...
// if word is new, store in array
{
array1 = realloc(array1, (ndx+1)*sizeof*array1); // one more string
if (!array1) exit(1); // out of memory
array1[ndx++] = strdup(word); // store word's copy
*array1a = array1; // return new array
}
return ndx; // return count
}
Ok so I have the below code and I am just pulling various things from a file and inputing them in an array of structs, it "seemingly" works initially, BUT when I go to printing it after it is done with the file it seemed to have replaced all of the courses and names with the very last vale, oddly this doesnt happen with the integers (grades), the grades do get inputed properly.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student {
char *name;
char *course;
int grade;
};
void courseSort(struct student d[20], int size);
int main(void)
{
FILE* fp;
char* filename = "grades.csv";
char buffer[100];
char* name, *class;
char* del=",";
int grade, i, counter=0;
struct student d[20];
if((fp=fopen(filename, "r"))==NULL)
{
printf("unable to open %s\n", filename);
exit(1);
}
while(fgets(buffer, sizeof(buffer), fp) !=NULL)
{
name = strtok(buffer,del);
class=strtok(NULL,del);
grade = atoi(strtok(NULL,del));
d[counter].name=name;
d[counter].course=class;
d[counter].grade=grade;
printf("%s %s %d\n",d[counter].name,d[counter].course,d[counter].grade);
counter++;
}
printf("\n\n\n");
for(i=0;i<counter;i++)
printf("%s %s %d\n",d[i].name,d[i].course,d[i].grade);
courseSort(d,counter);
fclose(fp);
}
I am not sure what I am doing wrong :( it all seems straightforward but not sure why it just replaces everything with the latest one.
The strtok returns a pointer to the buffer and does not allocate memory. Since you do not copy the strings, you end up with lots of pointers pointing to the same buffer that is overwritten at each iteration of the loop.
To fix this, you need to change your loop to copy the strings using strdup:
while(fgets(buffer, sizeof(buffer), fp) != NULL)
{
d[counter].name = strdup(strtok(buffer, del));
d[counter].course = strdup(strtok(NULL, del));
d[counter].grade = atoi(strtok(NULL, del));
counter++;
}
Don't forget to return the allocated memory with free once you no longer need the strings:
for (i = 0; i < counter; i++) {
free(d[i].name);
free(d[i].course);
d[i].name = NULL;
d[i].course = NULL;
}
Note that strdup is part of POSIX1.2001 standard, not part of C89. If it is not available, you'll have to re-implement it yourself (quite easy):
char *my_strdup(const char *str) {
char *copy;
size_t len = strlen(str) + 1;
if (len == 0) return NULL;
copy = (char *)malloc(len);
if (copy == NULL) return NULL;
memcpy(copy, str, len);
return copy;
}
This is a simple misunderstanding of pointers and char arrays (strings). Here are a couple pages that explains them pretty well:
http://www.cplusplus.com/doc/tutorial/pointers/
http://www.cplusplus.com/doc/tutorial/ntcs/
In your case, you are setting your struct pointer values equal to the returned pointer from strtok. A lot of those string functions work by putting the result at a certain memory address and returning the pointer to it. The pointer returned is always the same, so all your struct values are going to point to the last result of the strtok call.
This is why you need strdup (String duplicate). Basically it takes the value at the address given and copies the contents into a new place in memory and returns the value.
The error is here.
d[counter].name=name;
replace with:
d[counter].name = strdup(name); /*don't forget to free this memory.*/
the issue for the courses is the same.
This might be a very inefficient way to do it, but its sort of working
This code reads through a file, stores 8 line of text at a time in a global array (Would like a better option to do this if possible ) and dispatches for further processing.
here's the code
int count = 0; //global
char *array_buffer[8]; //global
void line(char *line_char)
{
int lent = strlen(line_char);
array_buffer[count] = line_char;
printf("%d\n",count);
if (count == 8)
{
int row,col;
for(row = 0; row<count; row++){
printf("%d\n",row);
for(col = 0; col<=lent; col++) {
printf("%c", array_buffer[row][col]);
}
printf("\n");
}
count = 0;
}
count++;
}
int main(int argc,char **argv)
{
clock_t start = clock();
FILE *fp = fopen(argv[1], "r");
if(fp == NULL )
{
printf("Couldn't open file %s",argv[1]);
}
char buff[512];
while (fgets(buff, 512, fp) != NULL )
{
line(buff); /*sending out an array having one line*/
}
return 0;
}
The issue is that while printing out the contents of array_buffer, its printing out the last line in the buffer 8 times. (i.e. the 8th line its reading in every cycle). Its pretty obvious that
array_buff[0]
....
array_buff[7]
all point to the address of line 8
any help in solving this ? I know it might not be the correct way to buffer something at all !
The problem with your approach that leads to the behavior that you see is that your code never copies the data from the buffer. This line
array_buffer[count] = line_char;
puts a pointer to the same char buff[512] from main at all eight locations. Subsequent calls to fgets override the content of previous reads, so you end up with eight copies of the last line.
You can fix this issue by making a copy, e.g. with strdup or by allocating memory with malloc and making a copy. You need to free everything that you allocate, though.
void line(char *line_char){
if (count == 8){
int row,col;
for(row = 0; row<count; row++){
printf("%2d:",row);
printf("%s", array_buffer[row]);
free(array_buffer[row]);
}
count = 0;
}
int lent = strlen(line_char);
array_buffer[count] = malloc((lent + 1)*sizeof(char));
strcpy(array_buffer[count], line_char);
//printf("%d\n", count);
count++;
}
You have a stale pointer, here I will explain
while (fgets(buff, 512, fp) != NULL )
{
//buff updated
line(buff);
//...
//inside of the line function
somepointertopointers[currIndex]=buff;
now it is looking at the location at buff, so all of the elements are looking at the same location, you need to copy the chars, or make a longer buffer and make sure you are updating the location the pointer is looking at, you can make 8 separate char[] pointers as well
This will give you the result you want
buff[512][8];
char** curr = buff;
while(fget(*curr,512,fp)!= NULL)
{
line(*curr);
curr++;
}
or alternatively you could allocate the buffer that is passed
#def BUFF_SIZE 512
#def BUFF_ARRAY_LEN 8
//put this somewhere before calling line
//to initialize your array_buffer
for(i=0;i<BUFF_ARRAY_LEN;i++)
{
array_buffer[i]=NULL;
}
...
//update in function line
//makes more sense to just use
//the max len of a line
if(array_buffer[count] == NULL)
array_buffer[count]=(char*)malloc(sizeof(char)*BUFF_SIZE);
strcpy(array_buffer[count],line_char);
...
//you will also need to
//clean up after you are
//done with the memory
for(i=0;i<BUFF_ARRAY_LEN;i++)
{
free(array_buffer[i]);
}