This question already has answers here:
Nested strtok function problem in C [duplicate]
(2 answers)
What are the differences between strtok and strsep in C
(3 answers)
Closed 5 years ago.
I have a function that takes a string. I make a copy of the string and a char *token;. I say token = strtok(myString, " "); and then start a while loop with while(token != NULL) {...}, and I do some stuff in the while loop. At the end of the while loop I say token = strtok(NULL, " "); which seems to follow examples I've seen online. My input string is "this is a string I will mess with" and my first token is "this" which is great, but then after I say token = strtok(NULL, " "); token appears to be "(null)" instead of "is". I am wondering why. Here is my code:
int numReps2(char *s) {
printf("inside numReps2 with \"%s\"\n", s);
// iterate through s until you find a space
// do matchingwords with the word and an accumulator string that you build char x char
int l = mystrlen(s);
char *copy = malloc(1+(sizeof(char) * l));
mystrcpy(copy, s);
char *accu = malloc(1+(sizeof(char) * l));
int ret = 0;
printf("about to tokenize \"%s\"\n", copy);
char *token;
const char delim[2] = " ";
token = strtok(copy, delim);
while(token != NULL) {
if(matchingWords(accu, copy)) {
printf("match-> accu is \"%s\" and token is \"%s\"\n", accu, token);
// we have a match
ret++;
} else {
// no match, add to accu
char *temp = mystrappend(token, " "); // stick a space after it
printf("no match, adding \"%s\" to \"%s\", token is still \"%s\"\n", temp, accu, token);
accu = mystrappend(accu, temp);
printf("accu is now \"%s\"\n", accu);
free(temp);
printf("after freeing temp, token is \"%s\"\n", token);
}
printf("here is what's after token: %c\n", *(token+strlen(token)+2));
token = strtok(NULL, delim);
printf("just tok'd again, now it's \"%s\"\n", token);
}
free(copy);
free(accu);
return ret;
}
Related
I have to write this code, I mean I should read from the file name of students and their mark, and then sort students by the grow of mark. Now I just want to output only mark. I want to display grades using structures. I don't know where the problem is.
text.file
Jon 3
Alina 5
Ron 1
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS
#include <string.h>
#include <stdlib.h>
int main()
{
const int N = 3;
int i = 0;
struct student {
char surname[50];
int mark;
};
struct student PI1[N];
char str[50];
const char s[1] = " ";
char* token;
FILE* ptr;
token = strtok(str, s);
ptr = fopen("test.txt", "r");
if (NULL == ptr) {
printf("file can't be opened \n");
}
while (fgets(str, 50, ptr) != NULL){
token = strtok(str, s);
strcpy(PI1[i].surname, token);
token = strtok(NULL, s);
PI1[i].mark = atoi(token);
i++;
}
fclose(ptr);
printf("The marks is:\n");
printf("%d %d %d", PI1[0].mark, PI1[1].mark, PI1[2].mark);
return 0;
}
You need to prevent the program from reading from the file pointer if opening the file fails:
ptr = fopen("test.txt", "r");
if (NULL == ptr) {
perror("test.txt");
return 1; // this could be one way
}
The second argument to strok should be a null terminated string. const char s[1] = " "; only has room for one character. No null terminator (\0). Make it:
const char s[] = " "; // or const char s[2] = " "; or const char *s = " ";
Don't iterate out of bounds. You need to check so that you don't try to put data in PI1[N] etc.
while (i < N && fgets(str, sizeof str, ptr) != NULL) {
// ^^^^^^^^
Check that strok actually returns a pointer to a new token. If it doesn't, the line you've read doesn't fulfill the requirements.
while (i < N && fgets(str, sizeof str, ptr) != NULL) {
token = strtok(str, s);
if(!token) break; // token check
strcpy(PI1[i].surname, token);
token = strtok(NULL, s);
if (token) // token check
PI1[i].mark = atoi(token);
else
break;
i++;
}
You could also skip the strcpy by reading directly into your struct student since char str[50]; has the same length as surname. str should probably be larger though, but for now:
while (i < N && fgets(PI1[i].surname, sizeof PI1[i].surname, ptr) != NULL) {
token = strtok(PI1[i].surname, s);
if(!token) break;
token = strtok(NULL, s);
if (token)
PI1[i].mark = atoi(token);
else
break;
i++;
}
Only print as many marks as you successfully read
printf("The marks are:\n");
for(int idx = 0; idx < i; ++idx) {
printf("%d ", PI1[idx].mark);
}
putchar('\n');
I have been trying to write a code to remove a word from an inputted string as part of my homework. But the thing is the outputted "modified" string never really gets modified and it actually always outputs the inputted string. I'm new to strings so I don't have a perfect understanding of how the string.h library functions work.
#include<stdio.h>
#include<string.h>
int main(void)
{
char str[60], strtemp[60], word[10], * token;
printf("Enter the sentence: ");
gets_s(str);
printf("Enter the word to be deleted: ");
gets_s(word);
int i = 0;
token = strtok(str, " ");
while (token != NULL) {
if (!i && token != word)
strcpy(strtemp, token);
else if (token == word) {
token = strtok(NULL, " ");
continue;
}
else {
strcat(strtemp, " ");
strcat(strtemp, token);
}
token = strtok(NULL, " ");
i++;
}
strcpy(str, strtemp);
printf("Modified string: %s \n", str);
}
Add the following:
char *strremove(char *str, const char *sub) {
size_t len = strlen(sub);
if (len > 0) {
char *p = str;
while ((p = strstr(p, sub)) != NULL) {
memmove(p, p + len, strlen(p + len) + 1);
}
}
return str;
}
You should use the memmove() (written on your post's comment too.)
Reference: this thread of SO.
I'm writing a compiler for assembler, and I need to do parsing to the text I get from a file, without making any changes in the original String. The function I used to copy the String was strcpyto a buffer, and to cut the String was strtok to cut the buffer.
Everything works perfect, but once I try to cut the original String after using the function addressingConstantIndex , I get null.
I tried to change the type of the buffer to a pointer of Character, it didn't really work. I guess the main problem it's in the way I copy the original String to the buffer.
int main(){
char desti[MAXCHAR];
char *temp;
char *token;
temp = "mov LIST[5] , r4";
strcpy(desti,temp);
printf("\ndest is : %s\n", desti);
token = strtok(desti," ");
printf("\nthe Token in Main is : %s \n", token);
token = strtok(NULL, ",");
printf("\nthe Token in Main is : %s\n", token);
printf("\nThe value is %d \n ",addressingConstantIndex(token));
token = strtok(NULL, " ,");
printf("\nthe Token in Main is : %s\n", token);
return 0;
}
int addressingConstantIndex(char * str) {
char buf[43];
char *token;
int ans;
strcpy(buf, str);
token = strtok(buf, "[");
printf("The string is %s\n",str);
if (token != NULL)
{
printf("the token is %s\n", token);
token = strtok(NULL, "]");
printf("the token is %s\n", token);
if(isOnlyNumber(token))
{
token = strtok(NULL, " ");
if (checkIfSpaces(token,0) == ERROR)
{
printf("ERROR: Extra characters after last bracket %s \n", str);
ans = ERROR;
} else
ans = OK;
} else {
printf("ERROR: Unknown string - %s - its not a macro & not a number.\n", token);
ans = ERROR;
}
} else {
printf("ERROR: %s , its not a LABEL", token);
ans = ERROR;
}
return ans;
}
int isOnlyNumber(char *str) {
int i, isNumber;
i = 0;
isNumber = 1;
if (!isdigit(str[i]) && !(str[i] == '-' || str[i] == '+'))
{
isNumber = ERROR;
}
i++;
while (i < strlen(str) && isNumber == 1)
{
if (!(isdigit(str[i]))) {
if (isspace(str[i]))
isNumber = checkIfSpaces(str, i);
else
isNumber = ERROR;
}
i++;
}
return isNumber;
}
int checkIfSpaces(char *str, int index) {
int i;
if (str == NULL)
{
return OK;
} else {
for (i = index; i < strlen(str); i++)
{
if (!isspace(str[i])) return ERROR;
}
}
return OK;
}
The expecting result:
dest is : mov LIST[5] , r4
the Token in Main is : mov
the Token in Main is : LIST[5]
The string is LIST[5]
the token is LIST
the token is 5
The value is 1
the Token in Main is : r4
The real result:
dest is : mov LIST[5] , r4
the Token in Main is : mov
the Token in Main is : LIST[5]
The string is LIST[5]
the token is LIST
the token is 5
The value is 1
the Token in Main is : (null)
The difference It's in the last row of the result.
The problem is that strtok() maintains a single static pointer to the current string location. So in addressingConstantIndex(), you start processing the local buf, so that when you return to main() it is no longer parsing desti but the now out of scope buf from addressingConstantIndex().
The simplest change to your existing code would be to use strtok_r() (or strtok_s() on Windows):
char* context = 0 ;
token = strtok_r( desti, " ", &context ) ;
printf("\nthe Token in Main is : %s \n", token);
token = strtok_r( NULL, ",", &context ) ;
...
Then similarly in addressingConstantIndex():
char* context = 0 ;
token = strtok_r(buf, "[", &context);
...
strtok replaces the separator in the string with a NULL on each call. Your code is finding the 'LIST[5]' token at the main level, at which point it has replaced the ',' with a NULL.
In addressingConstantIndex, strtok is reset with a new string and parses correctly (though your function is typed void instead of int).
At at the main level again, strtok is not being reset, so it is continuing to parse the string used in addressingConstantIndex.
To fix this you need to reset strtok again to continue. However you can't just call it with strtok(desti,",") as the desti has all the separators set to NULL from previous calls.
A quick solution is to copy the token to feed into addressingConstantIndex at the main level, and complete the main level before parsing at the next level.
int main(){
char desti[MAXCHAR];
char *temp;
char *token;
temp = "mov LIST[5] , r4";
strcpy(desti,temp);
printf("\ndest is : %s\n", desti);
token = strtok(desti," ");
printf("\nMnemonic : %s \n", token);
token = strtok(NULL, ",");
printf("\nLIst bit: %s\n", token);
char buf[80]; //Save the token
strcpy(buf, token);
token = strtok(NULL, " ,"); //Finish this level of processing
printf("\nRegister: %s\n", token);
//Continue at the next level with copy
printf("\nThe value is %d \n ",addressingConstantIndex(buf));
return 0;
}
Though a strtok_r solution might suit your needs better going forward
This is a sample from my code that im working on. However i have been trying to understand what to input where I wrote "XXXX" in my sorting alghoritm. Any ideas? Am i doing something or right or is it all wrong. The only error that occurs is "XXXX" is undeclared, but i can not figure out the right input.(the struct name and the "if" is somewhat "hard to read", due to my limited skills in creating code on this website)
code:
#define NUM_ITEMS sizeof(items) / sizeof(items[0]) struct vara
{
int nummer;
char namn[100];
float pris;
float volym;
char typ[100];
char stil[100];
char forpackning[20];
char land[20];
char producent[50];
float alkoholhalt;
} items[100]; for (i = 0; i < 100 && fgets(envara, 512, fp); i++)
{
envara[strlen(envara) - 1] = '\0';
oneline = strdup(envara);
tok = strtok(oneline, delim);
items[i].nummer = atoi(tok);
tok = strtok(NULL, delim);
strncpy(items[i].namn, tok, (max(strlen(tok), sizeof(items[0].namn))));
tok = strtok(NULL, delim);
items[i].pris = atof(tok);
tok = strtok(NULL, delim);
items[i].volym = atof(tok);
tok = strtok(NULL, delim);
strncpy(items[i].typ, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].stil, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].forpackning, tok, strlen(tok));
tok = strtok(NULL, delim);
strncpy(items[i].land, tok, min(strlen(tok), sizeof(items->land)));
tok = strtok(NULL, delim);
strncpy(items[i].producent, tok, strlen(tok));
tok = strtok(NULL, delim);
items[i].alkoholhalt = atof(tok);
int nummer_sortering(const void* n1, const void* n2)
{
items *XXXX = n1;
items *XXXX = n2;
return items->nummer - items->nummer;
}
printf("\n\nItems sorted by number:");
qsort(items, NUM_ITEMS, sizeof(items), nummer_sortering);
for (i = 0; i < NUM_ITEMS; i++)
printf("nummer: %d\n"
"namn: %s\n"
"pris: %f\n"
"volym: %f\n"
"typ: %s\n"
"stil: %s\n"
"forpackning: %s\n"
"land: %s\n"
"producent: %s\n"
"alkoholhalt: %f\n\n",
items[i].nummer,
items[i].namn,
items[i].pris,
items[i].volym,
items[i].typ,
items[i].stil,
items[i].forpackning,
items[i].land,
items[i].producent,
items[i].alkoholhalt);
Your comparing function is wrong, the compiler should have given you an error.
What kind of type is items? You cannot declare a variable with the name of
another variable. It should be:
int nummer_sortering(const void* n1, const void* n2)
{
struct vara *item1 = n1;
struct vara *item2 = n2;
return n1->nummer - n2->nummer;
}
Also doing
envara[strlen(envara) - 1] = '\0';
is not correct. If the input is longer than 511 characters,
envara[strlen(envara) - 1] won't be a newline. The best way to remove the
newline (if any) is:
envara[strcspn(envara, "\n")] = '\0';
Also
strncpy(items[i].producent, tok, strlen(tok));
You are using strncpy wrong. The point of strncpy is that you limit the
numbers of bytes to copy based on the size of the destination. You are limiting the
number bytes based on the length of the source. If it's longer than what
producent can hold, then you will have a buffer overflow. Also remember that
strncpy does not necessarily write the '\0'-terminating byte. You have to
make sure that for yourself:
size_t max = sizeof items[i].producent;
strncpy(items[i].producent, tok, max);
items[i].producent[max - 1] = '\0';
Also you should check that strtok doesn't return NULL. If it does, that
means that the line does not have the format you're looking for. In this case
you can dismiss the line and read the next one.
Here is part of my code:
int main ()
{
char *sentence;
char *token;
int counter = 1;
sentence = (char*)malloc(255*sizeof(char));
scanf("%[^\n]s", sentence);
token = strtok(sentence, " ");
char *temp;
while(token != NULL)
{
printf("Token %d: %s\n", counter, token);
token = strtok(NULL, " ");
//temp = token;
//temp = strtok(NULL, " ");
counter++;
}
return 0;
}
If I run this when i type: "why herrow there" it gives me:
Token 1: why
Token 2: herrow
Token 3: there
If I uncomment the temp then it only gives me:
Token1: why
Token 2: herrow
It seems even tho I think I didn't hinder my token value with temp, it still affects my token value. I don't want temp to have any affect on my original token. How do I do that?
You string has three words "why herrow there" The case when you add temp statements:
Step first:
token = strtok(sentence, " "); <-- sentence: `"why\0herrow there"`
// token = sentence
char *temp;
first iteration:
while(token != NULL) // token is not null <-------------------------------+
{ |
printf("Token %d: %s\n", counter, token); // first time print why |
|
token = strtok(NULL, " "); <-- sentence: `"why\0herrow\0there"` |//step-2
<-- token points to "herrow" substring (*)|
temp = token; <---temp = token |
temp = strtok(NULL, " "); <---sentence: `"why\0herrow\0there"` |//step-3
<-- temp = "there" sub string |//Last token
counter++; |-------------------------------+
}
second iteration of while loop:
while(token != NULL) // token is not null, it is pointing to sustring "herrow"
{
printf("Token %d: %s\n", counter, token); printing "herrow"
token = strtok(NULL, " "); <-- no next token, token becomes NULL //step-4
temp = token;
temp = strtok(NULL, " "); <-- no next token, so temp becomes NULL //step-5
counter++;
}
Third iteration token is NULL
While loop breaks!
So does it only prints:
Token1: why
Token 2: herrow
Based on comment!
token = strtok(sentence, " "); // first token
next_token = token;
while(next_token != NULL){
printf("Token %d: %s\n", counter, token);
if(next_token = strtok(NULL, " "))
token = next_token; //Last token in string
// here you have last token that is not NULL
counter++;
}
// next_token is NULL, but token is not NULL it is equals to last token in string
counter--;
printf("Token %d: %s\n", counter, token);
Code working.
You can achieve what you want by using the reentrant version of strtok() that is strtok_r():
#define _POSIX_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char * sentence = malloc(255 * sizeof(*sentence));
scanf("%[^\n]s", sentence);
{
int counter = 0;
char * sp1 = NULL;
char * token = strtok_r(sentence, " ", &sp1);
while (NULL != token)
{
counter++;
printf("Token %d: %s\n", counter, token);
token = strtok_r(NULL, " ", &sp1);
{
char * sp2 = sp1;
char * temp = strtok_r(NULL, " ", &sp2);
if (NULL != temp)
{
printf("Temp %d: %s\n", counter, temp);
temp[strlen(temp)] = ' ';
}
}
}
}
return 0;
}
Note: This only work if the set of delimiters passed to strtok() only contains one characters (' ' in this example).