i have the following code:
char* pathTokens;
char* paths;
paths = getFilePaths();
//printf("%s", paths);
pathTokens = strtok(paths, "\n");
updateFile(pathTokens, argv[1]);
and these variables in the same file as updateFile():
static FILE* file;
static char content[1024];
static char* token;
static int numChanges = 0;
static char newContent[1024];
Here is updateFile():
void updateFile(char pathTokens[], char searchWord[]) {
while(pathTokens != NULL) {
printf("Token: %s\n", pathTokens);
updateNewContent(pathTokens, searchWord);
pathTokens = strtok(NULL, "\n");
}
}
and updateNewContent():
static void updateNewContent(char fileName[], char searchWord[]) {
if(searchWord == NULL) {
printf("Please enter a word\n");
return;
}
numChanges = 0;
file = fopen(fileName, "r");
if(file == NULL) {
printf("Error opening file\n");
return;
}
while(fgets(content, 1024, file) != NULL) {
token = strtok(content, " ");
}
fclose(file);
}
whenever token = strtok(content, " "); is called, the value of pathTokens changes. if i comment it out, pathTokens maintains its original values. i don't want pathTokens to change, so why is strtok modifying it?
You are nesting strtok calls and strtok doesn't work like that. For nesting
calls you have to use strtok_r.
Also, when calling strtok, only the first time the source argument must be
used, for all subsequent calls, NULL has to be used. When you call strtok
again with an non-NULL argument, strtok "forgets" about the last state and
"restarts" parsing new content.
When you do in updateNewContent you are doing:
while(fgets(content, 1024, file) != NULL) {
token = strtok(content, " ");
}
strtok will forget about paths (the very first call). Also this loop is
pointless, you read a line, you split it for the first time, and then read the
next line, split it again, etc. You are doing nothing with token. When the
loop ends token will store the first word of the last line.
And then the function returns and you do
pathTokens = strtok(NULL, "\n");
Because you call it with NULL, it will look continue parsing the contents
pointed to by content, which seems to be a global variable.
whenever token = strtok(content, " "); is called, the value of pathTokens changes
Of course it does, after updateNewContent returns, you assign a new value to
it. What else did you expect?
I really don't know what you are trying to do here, to me that makes no sense.
If you need to do a strtok with a token that previously returned by another
strtok, then you have to use strtok_r.
Here is an example of how to nest strtok:
char line[] = "a:b:c,d:e:f,x:y:z";
char *s1, *s2, *token1, *token2, *in1, *in2;
in1 = line;
while(token1 = strtok_r(in1, ",", &s1))
{
in1 = NULL; // for subsequent calls
in2 = token1;
printf("First block: %s\n", token1);
while(token2 = strtok_r(in2, ":", &s2))
{
in2 = NULL; // for subsequent calls
printf(" val: %s\n", token2);
}
}
Output:
First block: a:b:c
val: a
val: b
val: c
First block: d:e:f
val: d
val: e
val: f
First block: x:y:z
val: x
val: y
val: z
If you use strtok() function it means that you want to divide your input into tokens. Like that when you given input strtok(pathtokens,"") ,divides into tokens and prints even though there is pointer variable
Related
I have a problem with code. I want to take first and last words from each lines from txt file. I wrote this code for now:
void StartEnd(char * word)
{
FILE* fp;
fp = fopen("linie.txt", "r");
if (fp == NULL) {
printf("! ");
return;
}
char store[MAX_LINE];
while (fgets(store, MAX_LINE - 1, fp)){
char * FirstWord = strtok(store," ");
char * LastWord;
char * token = strtok (store," ");
while (token != NULL){
LastWord = token;
token = strtok (NULL," ");
}
printf("%s\n",LastWord);
}
fclose(fp);
}
It is working for last word but only if i dont use FirstWord with strtok and i dont know why :(.
I would be grateful for any answer. Thanks!
On a first call, the function expects a C string as argument for str,
whose first character is used as the starting location to scan for
tokens. In subsequent calls, the function expects a null pointer and
uses the position right after the end of the last token as the new
starting location for scanning.
On subsequent calls to strtok, you are supposed to pass a NULL pointer. Doing that makes the function work properly:
void StartEnd(char * word)
{
FILE* fp;
fp = fopen("linie.txt", "r");
if (fp == NULL) {
printf("! ");
return;
}
char store[MAX_LINE];
while (fgets(store, MAX_LINE - 1, fp)){
char * FirstWord = strtok(store," ");
char * LastWord;
char * token = strtok (NULL, " ");
while (token != NULL){
LastWord = token;
token = strtok (NULL," ");
}
printf("%s\n",LastWord);
printf("%s\n",FirstWord);
}
fclose(fp);
}
Keep in mind that fgets reads n-1 characters or until it encounters a newline or EOF character. however a newline character is also considered a valid character and stored in the string. That means LastWord may end with a newline character. To fix that, pass " \n" instead of " " to strtok. Then both a newline and a blankspace are considered delimiters.
I use the following code to parse this CSV
me;val1;val2;val3;val4;val5;
me;val1;val2;val3;val4;val5;
void readcsv()
{
FILE* stream = fopen("input.csv", "r");
char line[1024];
while (fgets(line, 1024, stream))
{
char* tmp = strdup(line);
// printf("Field 1 would be %s\n", getcsvfield(tmp, 1));
printf("Field 1 would be %s\n", getcsvfield(tmp, 1));
printf("Field 2 would be %s\n", getcsvfield(tmp, 2));
// NOTE strtok clobbers tmp
free(tmp);
}
}
//Used for parsing CSV
const char* getcsvfield(char* line, int num)
{
const char* tok;
for (tok = strtok(line, ";");
tok && *tok;
tok = strtok(NULL, ";\n"))
{
if (!--num)
return tok;
}
return NULL;
}
But i keep getting NULL value in the second Field
Output:
Field 1 would be me
Field 2 would be (null)
Field 1 would be me
Field 2 would be (null)
What im i doing wrong?
strtok(line, ";");
strtok modifies the string (in this case line) being passed to it. So you should not use the same line (returned from first call to getcsvfield) also during the second time you call getcsvfield, because after first call to getcsvfield, line now has different content.
Notice this is not an issue within the function getcsvfield, because when you pass NULL second time to strtok inside that function, it knows how to proceed with the modified input string in a correct way.
From manual about strtok parameters:
Notice that this string is modified by being broken into smaller
strings (tokens).
Something like this should do the trick. This is the most "basic" approach, you can try other ones too. Leave the getcsvfield function as you had in your code initially, just on the caller side do:
char line[1024];
char buffer[1024];
while (fgets(line, 1024, stream))
{
// char* tmp = strdup(line); not necessary in this case
strcpy(buffer, line);
printf("Field 1 would be %s\n", getcsvfield(buffer, 1));
strcpy(buffer, line);
printf("Field 2 would be %s\n", getcsvfield(buffer, 2));
// free(tmp);
}
As it currently stands above, each call to getcsvfield, returns poitner to same memory address - buffer. For printing it works ok (because at the time of printing it shows what is there in buffer), but if you want to store result of each call to getcsvfield for later use, you may want to copy result of each invocation of getcsvfield to some different memory location each time.
I am trying to split a string into two tokens using strtok() that might have spaces and tabs mixed in the string.
So I made this:
struct strstr
{
char *str,
*one,
*two;
};
typedef struct strstr *STRSTR;
void split(STRSTR);
int main()
{
STRSTR str = malloc(sizeof(struct strstr));
str->str = malloc(256);
fgets(str->str, 256, stdin);
split(str);
printf("%s, %s\n", str->one, str->two);
free(str->str);
free(str);
return 0;
}
void split(STRSTR str)
{
int i;
char *temp = str->str;
while(isspace(*(str->str)))
str->str++;
str->one = strtok(str->str, " \t");
for(i = 0; i < strlen(str->one); i++)
{
if(!isspace(str->one[i]))
str->str++;
}
str->str++;
if(str->str != NULL)
{
puts("In null if");
str->two = strtok(str->str, "");
}
str->str = temp;
}
So for example if you input Hello Earth lingss, it will print out Hello, Earth lingss, which is perfect.
However, if I input Hello only, the split function goes inside the if(str->str != NULL) statement. How do I stop it from doing that with the code that I have?
EDIT: Also another problem, if someone doesn't mind checking it out. temp will only point to the first word in str->str. How can I make it point to the whole thing?
Add this statement before the last if block in the split function
str->str = strtok(str->str," \t"); like
str->str = strtok(str->str," \t");
if(str->str != NULL)
{
puts("In null if");
str->two = strtok(str->str, "");
}
you split the string based on the "\t" as the delimiter but you never changed the str->str string, use the above snippet and it should work fine
strtok is a funny function that both modifies the string you pass it, and stores information about it internally. You should pass your string to strtok once, then pass in NULL on subsequent calls. For instance, if your goal is to simply break a string up into tokens (which is obviously what strtok is for), then something like:
#define BUFFER_SIZE 256
int main(void) {
char *buffer = malloc(BUFFER_SIZE);
if (!buffer) {
return -1;
}
fgets(buffer, BUFFER_SIZE, stdin);
char *word;
char *ptr = buffer;
printf("Tokens: [");
while ((word = strtok(ptr, " \t\n"))) {
printf("%s, ", word);
ptr = NULL;
}
printf("]\n");
free(buffer);
}
will work. When I run the code like this:
./quick
when in the fun apple orange
I get the following result:
Tokens: [when, in, the, fun, apple, orange, ]
The important thing is that I only passed the buffer pointer to strtok on the first time through the loop. After that it is passed NULL.
Can't seem to work out why this code is not working.
It should be really straight forward.
From what I have troubleshooted, in the while(token) block the id array is assigned but then when I go to print all the char array's outside the while(token) block the id array displays nothing but all the other array's display their contents.
int loadCatData(char* menuFile) {
char line[MAX_READ];
char id[ID_LEN];
char hotCold[MIN_DESC_LEN];
char name[MAX_NAME_LEN];
char description[MAX_DESC_LEN];
char delim[2] = "|";
char lineTemp[MAX_READ];
int count;
FILE *mfMenu = fopen(menuFile, "r");
while (fgets(line, sizeof(line), mfMenu)!=NULL) {
count = 0;
printf(line);
strcpy(lineTemp,line);
char* token = strtok(lineTemp, delim);
while (token) {
printf(token);
if (count == 0) {
strcpy(id, token);
}
if (count == 1) {
strcpy(hotCold, token);
}
if (count == 2) {
strcpy(name, token);
}
if (count == 3) {
strcpy(description, token);
}
printf("\n");
token = strtok(NULL, delim);
count = count + 1;
}
printf(id);
printf(hotCold);
printf(name);
printf(description);
}
fclose(mfMenu);
return true;
}
You are the victim of a buffer overflow error caused by strcpy.
What is happening is that the hotCold array is too small to hold the data you're filling it with, but strcpy doesn't care, nor does it know that there isn't enough room. So it keeps on writing data into hotCold and then runs out of room, then fills up the padding bytes, then fills up id. You just have the unfortunate luck of having the terminating null byte of hotCold sitting at the start of id.
Switch from using strcpy to strncpy or strncat (which I think is better than strncpy). If you're skeptical of what I'm saying, add a line of code at the end that goes like this:
assert (strlen (hotCold) < MIN_DESC_LEN);
The other alternative is that the id field is being interpreted as a special printf-format specifier. Just in case, replace printf(id) with printf("%s", id).
int loadCatData(const char* menuFile) {
char id[ID_LEN];
char hotCold[MIN_DESC_LEN];
char name[MAX_NAME_LEN];
char description[MAX_DESC_LEN];
FILE *mfMenu = fopen(menuFile, "r");
while (fscanf(mfMenu, "%*s|%*s|%*s|%*s",
sizeof(id), id, sizeof(hotCold), hotCold,
sizeof(name), name, sizeof(description), description) == 4) {
printf("%s %s %s %s\n", id, hotCold, name, description);
}
fclose(mfMenu);
return true;
}
You should never pass input from outside the program to printf as the first argument. Imagine if one of the tokens is "%s" and you say printf(token)--that's undefined behavior because you didn't pass a second string to print, and your program will crash if you're lucky.
I've been reading up on strtok and thought it would be the best way for me to compare two files word by word. So far i can't really figure out how i would do it though
Here is my function that perfoms it:
int wordcmp(FILE *fp1, FILE *fp2)
{
char *s1;
char *s2;
char *tok;
char *tok2;
char line[BUFSIZE];
char line2[BUFSIZE];
char comp1[BUFSIZE];
char comp2[BUFSIZE];
char temp[BUFSIZE];
int word = 1;
size_t i = 0;
while((s1 = fgets(line,BUFSIZE, fp1)) && (s2 = fgets(line2,BUFSIZE, fp2)))
{
;
}
tok = strtok(line, " ");
tok2 = strtok(line, " ");
while(tok != NULL)
{
tok = strtok (NULL, " ");
}
return 0;
}
Don't mind the unused variables, I've been at this for 3 hours and have tried all possible ways I can think of to compare the values of the first and second strtok. Also I would to know how i would check which file reaches EOF first.
when i tried
if(s1 == EOF && s2 != EOF)
{
return -1;
}
It returns -1 even when the files are the same! Is it because in order for it to reach the if statement outside of the loop both files have reached EOF which makes the program always go to this if statement?
Thanks in advance!
If you want to check if files are same try doing,
do {
s1 = fgetc(fp1);
s2 = fgetc(fp2);
if (s1 == s2) {
if (s1 == EOF) {
return 1; // RETURN TRUE
}
continue;
}
else {
return -1; // RETURN FALSE
}
} while (1);
Good Luck :)
When you use strtok() you typically use code like this:
tok = strtok(line, " ");
while (NULL != tok)
{
tok = strtok(NULL, " ");
}
The NULL in the call in the loop tells strtok to continue from after the previously found token until it finds the null terminating character in the value you originally passed (line) or until there are no more tokens. The current pointer is stored in the run time library, and once strtok() returns NULL to indicate no more tokens any more calls to strtok() using NULL as the first parameter (to continue) will result in NULL. You need to call it with another value (e.g. another call to strtok(line, " ")) to get it to start again.
What this means is that to use strtok on two different strings at the same time you need to manually update the string position and pass in a modified value on each call.
tok = strtok(line, " ");
tok2 = strtok(line2, " ");
while (NULL != tok && NULL != tok2)
{
/* Do stuff with tok and tok2 here */
if (strcmp(tok, tok2)... {}
/* Update strtok pointers */
tok += strlen(tok) + 1;
tok2 += strlen(tok2) + 1;
/* Get next token */
tok = strtok(tok, " ");
tok2 = strtok(tok2, " ");
}
You'll still need to add logic for determining whether lines are different - you've not said whether the files are equivalent if a line break occurs at different position but the words surrounding it are the same. I assume it should be, given your description, but it makes the logic more awkward as you only need to perform the initial fgets() and strtok() for a file if you don't already have a token. You also need to look at how files are read in. Currently your first while loop just reads lines until the end of the file without processing them.