Here are some chunks of my code to give you a view of my problem
typedef struct {
int count;
char *item;
int price;
char *buyer;
date *date;
}transaction;
transaction *open(FILE *src, char* path) {
char buffer[100], *token;
int count = 0;
transaction *tlist = (transaction*)malloc(sizeof(transaction));
tlist = alloc(tlist, count);
src = fopen(path, "r");
if (src != NULL) {
printf("\nSoubor nacten.\n");
}
else {
printf("\nChyba cteni souboru.\n");
return NULL;
}
while (fgets(buffer, sizeof(buffer), src)) {
tlist = alloc(tlist, count+1);
token = strtok(buffer, "\t"); //zahodit jméno obchodníka
tlist[count].count = strtok(NULL, "x");
tlist[count].item = strtok(NULL, "\t");
tlist[count].item++;
tlist[count].item[strlen(tlist[count].item)] = '\0';
tlist[count].price = atoi(strtok(NULL, "\t "));
token = strtok(NULL, "\t"); //zahodit md
tlist[count].buyer = strtok(NULL, "\t");
tlist[count].date = date_autopsy(strtok(NULL, "\t"));
count++;
}
fclose(src);
return tlist;
}
transaction *alloc(transaction *tlist, int count) {
if (count == 0) {
tlist[0].item = (char*)malloc(20 * sizeof(char));
tlist[0].buyer = (char*)malloc(20 * sizeof(char));
}
else {
tlist = (transaction*)realloc(tlist, count * sizeof(transaction));
tlist[count - 1].item = (char*)malloc(20 * sizeof(char));
tlist[count - 1].buyer = (char*)malloc(20 * sizeof(char));
}
return tlist;
}
First in main(), I create the list
transaction *list = (transaction*)malloc(sizeof(transaction));
Then with the right command, I call my opening function that loads a file, then it tokens a line from that file into pieces that then puts into the structure. It all works fine.. When I want to print(for testing) tlist[count].item inside the opening function, it prints the right thing. But when I try it outside(in main()), it prints garbage. It somehow works for the date and price parts of sturcture.. I assume the "buyer" string will be broken as well. Thanks in advance
Since you are overwriting the allocated memory with the local buffer for the item , bueyr fields so it is not reflected in the caller function. Modify the code as below
tlist[count].count = strtok(NULL, "x") -> strcpy(tlist[count].count, strtok(NULL, "x"))
tlist[count].buyer = strtok(NULL, "\t") -> strcpy(tlist[count].buyer , strtok(NULL, "\t"))
and also check the tlist[count].date , You should allocate the memory for the date and also use memcpy to copy the contents.
Since strtok returns the NULL termintated string what is the use of the following lines ?
tlist[count].item++;
tlist[count].item[strlen(tlist[count].item)] = '\0';
strtok holds an internal static buffer which is filled and returned after every call.
somerthing like this:
char *strtok(...)
{
static char buf[XX];
// get next token
return buf;
}
You are effectively assigning and therefore overwriting the data the pointer points to after every call.
A better use would be to allocate memory for the char * fields and use strcpy for the data returned from strtok
Related
I'm working with c90 on linux.
I have a strange bug when I want to end a string,
let idx be the index, so when I get to the last index I want the list[idx] to be NULL.
example:
list[0] actually "hello"
list[1] actually "world\n"
list[2] sometimes is "" or NULL
so when I put NULL to the the end of the list its deletes one of the other words..
for: list[2] = NULL;
unexpectedly list[0] turns NULL but list[1] still "world\n" and list[2] of course NULL.
I wrote this function:
void function()
{
char buffer[BUFF_LEN];
char** list = NULL;
int list_len = 0;
while (fgets(buffer, BUFF_LEN, fptr))
{
list = (char**)malloc((sizeof(char*)));
get_input(buffer, list, &list_len);
/*
some other code
*/
}
free_list(list, list_len); /*free the array of strings words*/
}
and wrote also the get_input because I work with c90
void get_input(char* line, char** list, int *idx)
{
char * token;
*idx = 0;
token = strtok(line, " "); /*extract the first token*/
/* loop through the string to extract all other tokens */
while (token != NULL)
{
if (token && token[0] == '\t')
memmove(token, token + 1, strlen(token));
printf("%s\n", token);
list[*idx] = (char *)malloc(strlen(token)+1);
strncpy(list[*idx], token, strlen(token));
token = strtok(NULL, " "); /*get every token*/
(*idx)++;
}
if (*idx == 0)
list = NULL;
list[*idx - 1][strcspn(list[*idx - 1], "\n")] = 0; /* remove the "\n" */
list[*idx] = NULL; /* to know when the list ends */
}
the free function:
void free_list(char** list, int list_len)
{
int i;
for(i= list_len - 1; i >= 0; i--)
{
list[i] = NULL;
free(list[i]);
}
}
You have multiple issues.
void function()
{
char buffer[BUFF_LEN];
char** list = NULL;
int list_len = 0;
while (fgets(buffer, BUFF_LEN, fptr))
{
list = (char**)malloc((sizeof(char*)));
get_input(buffer, list, &list_len);
/*
some other code
*/
}
free_list(list, list_len); /*free the array of strings words*/
}
You only allocate memory for 1 pointer.
You only free the pointers in the last list.
You never free the memory for list ifself.
You should not cast the return value of malloc and friends.
This should be changed like this:
void function()
{
char buffer[BUFF_LEN];
char** list = NULL;
int list_len = 0;
while (fgets(buffer, BUFF_LEN, fptr))
{
list = malloc((sizeof(char*)));
get_input(buffer, &list, &list_len);
/*
some other code
*/
free_list(list); /*free the array of strings words*/
free(list);
}
}
The freeing function is also broken:
void free_list(char** list, int list_len)
{
int i;
for( i= list_len - 1; i >= 0; i--)
{
list[i] = NULL;
free(list[i]);
}
}
You set the pointer within list to NULL before you free it. This causes a memory leak as the memory is not really freed.
You don't really need the length as you have added a sentinel. But that is not an error.
There is also no need to free the pointers backwards.
After cleanup the function could look like this:
void free_list(char** list)
{
while (list[i])
{
free(list[i]);
i++;
}
}
Now the biggest part:
void get_input(char* line, char** list, int *idx)
{
char * token;
*idx = 0;
token = strtok(line, " "); /*extract the first token*/
/* loop through the string to extract all other tokens */
while (token != NULL)
{
if (token && token[0] == '\t')
memmove(token, token + 1, strlen(token));
printf("%s\n", token);
list[*idx] = (char *)malloc(strlen(token)+1);
strncpy(list[*idx], token, strlen(token));
token = strtok(NULL, " "); /*get every token*/
(*idx)++;
}
if (*idx == 0)
list = NULL;
list[*idx - 1][strcspn(list[*idx - 1], "\n")] = 0; /* remove the "\n" */
list[*idx] = NULL; /* to know when the list ends */
}
You do not care about memory for the pointers in your list. That means you store the pointers in memory that you are not allowed to touch. By doing this you invoke undefined behaviour.
You must realloc the memory and for that you must be able to modify the passed pointer.
You should not cast the return values of malloc and friends.
You access illegal index values if *idx==0
You call strncpy with the length of the string without space for the 0 byte. That will cause the copy to be not nul terminated. Also there is no need to use strncpy over strcpy as you have reserved enough memory.
void get_input(char* line, char*** list, int *idx)
{
char *token;
char **list_local = *list; // Make things easier by avoiding one * within the function.
*idx = 0;
token = strtok(line, " "); /*extract the first token*/
/* loop through the string to extract all other tokens */
while (token != NULL)
{
if (token[0] == '\t') // No need to check for `token` again
memmove(token, token + 1, strlen(token));
printf("%s\n", token);
list_local[*idx] = malloc(strlen(token)+1);
strcpy(list_local[*idx], token);
token = strtok(NULL, " "); /*get every token*/
(*idx)++;
/* Increase array size to hold 1 more entry. */
/* That new element already includes memory for the sentinel NULL */
{
char ** temp = realloc(list_local, sizeof(char*) * (*idx));
if (temp != NULL)
list_local = temp;
// TODO: error handling ...
}
}
if (*idx != 0)
{
list_local[*idx - 1][strcspn(list_local[*idx - 1], "\n")] = 0; /* remove the "\n" */
}
list_local[*idx] = NULL; /* to know when the list ends */
*list = list_local;
}
my struct format is like this
struct {
char student_ID[11];
char full_name [MAX];
char program [MAX];
char year;
char e_mail [MAX*2];
char status;
} student_info;
And this is my function which tries to get one student information
void scanStudents(FILE *file, student_info *student) {
char get_line [500];
fgets(get_line,500,file);
char *ID = strtok(get_line,";");
strcpy(student->student_ID, ID);
char *NAME = strtok(get_line, ";");
strcpy(student->full_name, NAME);
char *PROGRAM = strtok(get_line,";");
strcpy(student->program, PROGRAM);
char *YEAR = strtok(get_line, ";");
strcpy(student->year,YEAR);
char *E_MAIL = strtok(get_line, ";")
strcpy(student->e_mail,E_MAIL);
char *STATUS = strtok(get_line,";");
strcpy(student->status, STATUS);
}
I open file in other function and by calling this function in that my aim is try to store student informations in one array which type is student_ınfo. The txt file contains many student information in type of
31300000010;DURU AY;Computer Engineering;2;duru.ay#tedu.edu.tr;
Here is an example including error fixes that #Retired Ninja and #Gerhardh stated.
The file content is:
31300000010;DURU AY;Computer Engineering;2;duru.ay#tedu.edu.tr;A
Now this one is for storing each field as string. The fixes I made to your code are the following:
student_info declaration and its definition are fixed.
Add MIN_STRING to comply min String length that is 1 character + 1 NULL termination.
Change the year and status fields so that they comply the minimum string space requirements.
Add some NULL checks, perhaps to avoid corrupted student info.
Used only one pointer temp to hold the pointer address which is returned by strtok function.
This sample code is for reference only. So you adapt the idea behind this code to your actual code.
#include <stdio.h>
#include <string.h>
// Minimum string size must be 2 in order to store 1 character + terminating (NULL or '\0') character
#define MIN_STRING 2
#define MAX (50+1) // 1 character space for the NULL terminator.
struct student_info {
char student_ID[12];
char full_name [MAX];
char program [MAX];
char year[MIN_STRING];
char e_mail [MAX];
char status[MIN_STRING];
};
const char *file_name = "students.txt";
int main(void) {
FILE *file_students = fopen(file_name, "r");
if(file_students == NULL) {
printf("The file named %s could not be read\n", file_name);
return 1; // Return with some failure code
}
char get_line[500];
char *temp = NULL;
struct student_info student;
fgets(get_line, 500, file_students);
temp = strtok(get_line,";");
strcpy(student.student_ID, temp);
// After the first invocation of strtok you must pust NULL
temp = strtok(NULL, ";");
// strtok returns NULL if there are not any tokens left
if(temp == NULL) {
puts("Student ID NULL");
return 1;
}
strcpy(student.full_name, temp);
temp = strtok(NULL,";");
if(temp == NULL) {
puts("Name NULL");
return 1;
}
strcpy(student.program, temp);
temp = strtok(NULL, ";");
if(temp == NULL) {
puts("Program NULL");
return 1;
}
strcpy(student.year,temp);
temp = strtok(NULL, ";");
if(temp == NULL) {
puts("Year NULL");
return 1;
}
strcpy(student.e_mail,temp);
temp = strtok(NULL,";");
if(temp == NULL) {
puts("E-mail NULL");
return 1;
}
strcpy(student.status, temp);
puts("Sample student information");
printf(
"ID: %s\nFull name: %s\nProgram: %s\nYear: %s\nE-mail: %s\nStatus: %s\n",
student.student_ID, student.full_name, student.program, student.year,
student.e_mail, student.status
);
// Close the file
fclose(file_students);
return 0;
}
This is the output of the sample code:
Sample student information
ID: 31300000010
Full name: DURU AY
Program: Computer Engineering
Year: 2
E-mail: duru.ay#tedu.edu.tr
Status: A
So I'm using strtok to split an char array by " ". Then I place each word I split into a function that will determine a value for the word based on a list. However everything I place the function call in middle of the while loop to of splitting the char array it stops.
Do I have to split the array, store it in another array and then go through the second array?
p = strtok(temp, " ");
while (p != NULL) {
value = get_score(score, scoresize, p);
points = points + value;
p = strtok(NULL, " ");
}
So as long as value = get_score(score, scoresize, p); is there the while loops break after the first word.
strtok() uses a hidden state variable to keep track of the source string position. If you use strtok again, directly or indirectly in get_score(), this hidden state will be changed as to make the call p = strtok(NULL, " "); meaningless.
Do not use strtok() this way, either use the improved version strtok_r standardized in POSIX, available on many systems. Or re-implement it with strspn and strcspn:
#include <string.h>
char *my_strtok_r(char *s, char *delim, char **context) {
char *token = NULL;
if (s == NULL)
s = *context;
/* skip initial delimiters */
s += strspn(s, delim);
if (*s != '\0') {
/* we have a token */
token = s;
/* skip the token */
s += strcspn(s, delim);
if (*s != '\0') {
/* cut the string to terminate the token */
*s++ = '\0';
}
}
*context = s;
return token;
}
...
char *state;
p = my_strtok_r(temp, " ", &state);
while (p != NULL) {
value = get_score(score, scoresize, p);
points = points + value;
p = my_strtok_r(NULL, " ", &state);
}
I've used strdup() in the past in the same way that I am using it here. I am passing token2 into strdup which is of type char * with a valid pointer in it, yet when I try to run the line "name = strdup(token2);" my program segfaults and I am quite unsure as to why. If anyone would be able to help me it would be greatly appreciated. I also realize that my code does not return a proper type yet, I am still working on writing all of it.
struct YelpDataBST* create_business_bst(const char* businesses_path, const char* reviews_path){
if(fopen(businesses_path,"r") == NULL || fopen(reviews_path,"r") == NULL)
return NULL;
FILE* fp_bp = fopen(businesses_path, "r");
FILE* fp_rp = fopen(reviews_path, "r");
struct YelpDataBST* yelp = malloc(sizeof(struct YelpDataBST*));
int ID = -1;
int tempID;
long int addressOffset;
long int reviewOffset;
char line[2000];
char line2[2000];
char temp[2000];
char temp2[2000];
char* token;
char* token2;
char* name;
int len;
BusList* busNode = NULL;
BusList* busList = NULL;
BusTree* busTreeNode = NULL;
BusTree* busTree = NULL;
ID = -1;
tempID = 0;
fgets(line,2000,fp_rp);
fgets(line2,2000,fp_bp);
fseek(fp_rp,0, SEEK_SET);
fseek(fp_bp,0,SEEK_SET);
int ct = 0;
while(!feof(fp_rp)){
len = strlen(line);
token = strtok(line, "\t");
//printf("line: %s\n", line);
token2 = strtok(line2, "\t");
tempID = atoi((char*)strdup(token));
if(ct == 0){
tempID = 1;
ct++;
}
if((ID != tempID || (ID < 0)) && tempID != 0){
if(tempID == 1)
tempID = 0;
token2 = strtok(NULL, "\t");
//name = strdup(token2);
reviewOffset = ftell(fp_rp);
if(tempID != 0)
reviewOffset -= len;
addressOffset = ftell(fp_bp);
ID = atoi((char*)strdup(token));
busList = BusNode_insert(busList, addressOffset, reviewOffset); //replace with create node for tree
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\t");
token2 = strtok(NULL, "\n");
fgets(line2,2000,fp_bp);
}
token = strtok(NULL, "\t");
token = strtok(NULL, "\t");
token = strtok(NULL, "\t");
token = strtok(NULL, "\t");
token = strtok(NULL, "\n");
fgets(line,2000,fp_rp);
}
//BusList_print(busList);
}
strdup(token) segfaulting is most likely explained by token being NULL. (You don't need to strdup here anyway). Change that piece of code to:
if ( token == NULL )
{
fprintf(stderr, "Invalid data in file.\n");
exit(EXIT_FAILURE); // or some other error handling
}
tempID = atoi(token);
However a greater problem with the surrounding code is that you are trying to use strtok twice at once. It maintains internal state and you can only have one strtok "in progress" at any one time. The second one cancels the first one. You'll have to redesign that section of code.
Also, while(!feof(fp_rp)) is wrong, and your yelp mallocs the wrong number of bytes (although in the code posted you never actually try to store anything in that storage, so it would not cause an error just yet).
I'm writing a short function to parse through a file by checking string tokens. It should stop when it hits "visgroups", which is the 9th line of the file I am using to test (which is in the buffer called *source). "versioninfo" is the first line. When I run this code it just repeatedly prints out "versioninfo" until I cancel the program manually. Why isn't the strtok function moving on?
I will be doing some different manipulation of the source when I reach this point, that's why the loop control variable is called "active". Would this have anything to do with the fact that strtok isn't thread-safe? I'm not using source in any other threads.
int countVisgroups(int *visgroups, char *source) {
const char delims[] = {'\t', '\n', ' '};
int active = 0;
char *temp;
while (!active){
temp = strtok(source, delims);
if (temp == NULL) {
printf("%s\n", "Reached end of file while parsing.");
return(0);
}
if (strncmp(temp, "visgroups", 9) == 0) {
active = 1;
return(0);
}
printf("%s\n", temp);
}
return(0);
}
Your delims array needs to be nul terminated. Otherwise how can strtok know how many separators you passed in? Normally you'd just use const char *delims = "\t\n " but you could simply add ..., 0 to your initializer.
After the first call to strtok with the string you want to tokenize, all subsequent calls must be done with the first parameter set to NULL.
temp = strtok(NULL, delims);
And no it probably doesn't have to do anything with thread safety.
Try to rewrite it like this:
int countVisgroups(int *visgroups, char *source) {
const char delims[] = {'\t', '\n', ' ', '\0'};
int active = 0;
char *temp;
temp = strtok(source, delims);
while (!active){
if (temp == NULL) {
printf("%s\n", "Reached end of file while parsing.");
return(0);
}
if (strncmp(temp, "visgroups", 9) == 0) {
active = 1;
return(0);
}
printf("%s\n", temp);
temp = strtok(NULL, delims);
}
return(0);
}