How to fix 'strtok destroy original string' in c - c

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

Related

How to read from the file and write it in the structure? I have a little trouble with my code

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');

How to get each token from strtok()? [duplicate]

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;
}

Counting the amount of string tokens in C

I need to make a program that will emulate the terminal of Linux. Since some system calls requires 1,2 or more arguments, I want to make sure that the number of parameters given are correct. I'm using strtok() to separate the call name from the arguments, but I need to know how many tokens strtok() created to compare it.
Here's and example code:
char *comand = (char*) malloc(sizeof(char)*100);
char *token;
char *path1 = (char*) malloc(sizeof(char)*100);
char *path2= (char*) malloc(sizeof(char)*100);
fgets(comand, 100, stdin);
printf( "\nYou entered: %s \n", comand);
token = strtok(comand ," ");
//Check the number of tokens and add a condition in each IF to match
if (strcmp("ls",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("cat",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("cp",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
token = strtok(NULL," ");
strcpy(path2,token);
}
else if (strcmp("mv",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
token = strtok(NULL," ");
strcpy(path2,token);
}
else if (strcmp("find",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("rm",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("mkdir",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("rmdir",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token);
}
else if (strcmp("quit",token) == 0) {
exit(0);
}
else print("Number of parameters do not match);
the only thing strtok() does is look for the next occurance of the delimiter and overwrite that character with a \0 and return the pointer with the offset added. the pointer is kept in a static variable that's why a subsequent call to it with a NULL for the char * will perform it on the last string used from the offset that the last delimiter was found.
this page has a very nice example:
http://en.cppreference.com/w/c/string/byte/strtok
If you only want to count the arguments it would be easier to use strchr(), this function searches for a character and returns a pointer to its location. you could use it like this.
unsigned int i = 0;
char *temp = token;
while ( (temp = strchr(temp, '') != NULL) ) {
++i;
}
this has the added benefit of not modifying your original char array while strtok() does!
I would handle this within the functions you create for each command.
you pass all options to the function and there you parse it. either with strtok() or whatever else you want to use.
This keeps it nice and clean within the sub-routines and you will always know what to expect.
if (strcmp("ls",token) == 0) {
token = strtok(NULL," ");
strcpy(path1,token); // I would maybe change the path variable name to args
ret = lscmd(path1);
if (ret == -1) {
// invalid input detected
}
}
then you would have a ls function
int lsdcmd(char *args) {
// parse args for argumants you are looking for, return -1 if it fails
// do whatever you want to do.
}
You can count the arguments using strtok this way:
Example:
const char* delimiter = ",";
char* tokens[MAX_NUM_OF_ARGS];
unsigned int index = 0;
char* temp = strtok(buff,delimiter);
while (temp!=NULL){
if(index<MAX_NUM_OF_ARGS){
tokens[index]=temp;
}
index++;
temp = strtok(NULL,delimiter);
}
Then later you can iterate through the array of pointers (tokens) and compare them...

How to store temp value for strtok without changing the initial token

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).

array value over write

hello every one i am facing a strange problem i my code i am reading values from the file and file has rows and columns i am storing the second column in my array
but the problem i that when i first time copy in my position encryption[0] = token
i works well when i store at position encryption [1] my encryption [0] value over write with and become the same as encryption[1] the again at third loop encryption [0] ,encryption [1] become same as encryption [2] so in the end all values become the last sored value
here is my code kingly help me
#include<stdio.h>
#include <sys/stat.h>
#include<time.h>
#include<string.h>
void main()
{
FILE * f1 = fopen("2.19.18.110_202.142.175.104.csv" , "r");
if(f1==NULL)
{
printf("not open");
}
char ch;
int row =0;
int col=0;
while(ch!=EOF)
{
ch = fgetc(f1);
if(ch=='\n')
row++;
if(ch==' ')
col++;
}
fclose(f1);
int b=0;
int gg=0;
//for( b=0;b<row;b++)
char * encryption[row];
char payload[col*10];
FILE * f2 = fopen("2.19.18.110_202.142.175.104.csv" , "r");
while( fgets ( payload, sizeof payload, f2)!=NULL)
{
int col1=0;
printf("b= %d\t" , b);
// fgets ( payload, sizeof payload, f2);
fputs(payload ,stdout);
printf("\n\n");
char *token;
token = strtok(payload, " ");
token = strtok(NULL, " ");
encryption[gg] = token;
printf("token %s\n" ,token);
gg=gg+1;
printf("encryption %s\n" ,encryption[0]);
printf("encryption %s\n" ,encryption[1]);
printf("encryption %s\n" ,encryption[2]);
printf("encryption %s\n" ,encryption[3]);
token = strtok(NULL, " ");
while ( token != NULL)
{
token = strtok(NULL, " ");
}
}
}
encryption[] is just an array of pointers - for each element you need to malloc() sufficient memory (strlen(token) + 1 bytes) and then use strcpy() to copy the string contents from token to encryption[gg].
Change:
token = strtok(NULL, " ");
encryption[gg] = token;
to:
token = strtok(NULL, " ");
encryption[gg] = malloc(strlen(token) + 1);
strcpy(encryption[gg], token);
Don't forget to free() each element of encryption later when you're done, otherwise you'll leak memory.

Resources