Issues with strtok in C - c

I have the following function in my code to extract names:
void student_init(struct student *s, char *info) {
char *name;
char *name2;
char a[] = { *info };
name = strtok(a, "/");
strcpy(s->first_name, name);
name2 = strtok(NULL, "/");
strcpy(s->last_name, name2);
}
When I run this, I see:
Please enter a student information or enter "Q" to quit.
Daisy/Lee
A student information is read.: Daisy/Lee
Please enter a row number where the student wants to sit.
1
Please enter a column number where the student wants to sit.
2
The seat at row 1 and column 2 is assigned to the student: D . �
?.?. ?.?. ?.?.
?.?. ?.?. D.�.
?.?. ?.?. ?.?.
I am trying to use the strtok function in a c program to split a string with "/" to separate a fist and last name and store them in the first_name and last_name variables of a student strcuture. I can get the first name stored in the respective variable but as you can see from the image in the link above I am getting a ? symbol in the output where the first initial of the last name should be.

char a[] = { *info };
This is your problem. What this creates is a one-byte character array containing the first character of info and nothing else.
Since strtok requires a string, it's probably going to run off the end of that one-byte array and use whatever happens to be there in memory. That's why you're seeing the first character okay and not much else (though, technically, as undefined behaviour, literally anything is allowed to happen).
Rather than constructing a one-byte array, you should probably just use the string as it was passed in:
name = strtok(info, "/");
The only reason you would make a local copy is if the string you're tokenising was not allowed to be changed (such as if it were a string literal, or if you wanted to preserve it for later use). Since your sample run shows that you're reading into this string, it cannot be a string literal.
And, if you want to preserve it for later, that's probably a cost best incurred by the caller rather than the function (it's wasteful for the function to always make a duplicate when the information as to whether it's needed or not is known only to the caller).
In order to make a copy for tokenising, it's as simple as:
char originalString[] = "pax/diablo";
scratchString = strdup(originalString);
if (scratchString != NULL) {
student_init (struct student *s, scratchString);
free (scratchString);
} else {
handleOutOfMemoryIssue();
}
useSafely (originalString);
If your implementation doesn't have strdup (it's POSIX rather than ISO), see here.
In my opinion, a "cleaner" implementation would be along the lines of:
void student_init (struct student *s, char *info) {
// Default both to empty strings.
*(s->first_name) = '\0';
*(s->last_name) = '\0';
// Try for first name, exit if none, otherwise save.
char *field = strtok (info, "/");
if (field == NULL) return;
strcpy (s->first_name, field);
// Try for second name, exit if none, otherwise save.
if ((field = strtok (NULL, "/")) == NULL) return;
strcpy (s->last_name, field);
}

Related

How to store each sentence as an element of an array?

So, suppose I have an array (program asks me to write some text):
char sentences[] = "The first sentence.The second sentence.The third sentence";
And I need to store each sentence as an array, where I can have access to any word, or to store the sentences in a single array as elements.
(sentences[0] = "The first sentence"; sentences[1] = "The second sentence";)
How to print out each sentence separately I know:
char* sentence_1 = strtok(sentences, ".");
char* sentence_2 = strtok(NULL, ".");
char* sentence_3 = strtok(NULL, ".");
printf("#1 %s\n", sentence_1);
printf("#2 %s\n", sentence_2);
printf("#3 %s\n", sentence_3);
But how to make program store those sentences in 1 or 3 arrays I have no idea.
Please, help!
If you keep it in the main, since your sentences memory is static (cannot be deleted) you can simply do that:
#include <string.h>
#include <stdio.h>
int main()
{
char sentences[] = "The first sentence.The second sentence.The third sentence";
char* sentence[3];
unsigned int i;
sentence[0] = strtok(sentences, ".");
for (i=1;i<sizeof(sentence)/sizeof(sentence[0]);i++)
{
sentence[i] = strtok(NULL, ".");
}
for (i=0;i<sizeof(sentence)/sizeof(sentence[0]);i++)
{
printf("%d: %s\n",i,sentence[i]);
}
return 0;
}
In the general case, you first have to duplicate your input string:
char *sentences_dup = strdup(sentences);
sentence[0] = strtok(sentences_dup, ".");
many reasons for that:
you don't know the lifespan/scope of the input, and it is generally a pointer/a parameter, so your sentences could be invalid as soon as the input memory is freed/goes out of scope
the passed buffer may be const: you cannot modify its memory (strtok modifies the passed buffer)
change sentences[] by *sentences in the example above and you're pointing on a read-only zone: you have to make a copy of the buffer.
Don't forget to store the duplicated pointer, because you may need to free it at some point.
Another alternative is to also duplicate there:
for (i=1;i<sizeof(sentence)/sizeof(sentence[0]);i++)
{
sentence[i] = strdup(strtok(NULL, "."));
}
so you can free your big tokenized string at once, and the sentences have their own, independent memory.
EDIT: the remaining problem here is that you still have to know in advance how many sentences there are in your input.
For that, you could count the dots, and then allocate the proper number of pointers.
int j,nb_dots=0;
char pathsep = '.';
int nb_sentences;
int len = strlen(sentences);
char** sentence;
// first count how many dots we have
for (j=0;j<len;j++)
{
if (sentences[j]==pathsep)
{
nb_dots++;
}
}
nb_sentences = nb_dots+1; // one more!!
// allocate the array of strings
sentence=malloc((nb_sentences) * sizeof(*sentence));
now that we have the number of strings, we can perform our strtok loop. Just be careful of using nb_sentences and not sizeof(sentence)/sizeof(sentence[0]) which is now irrelevant (worth 1) because of the change of array type.
But at this point you could also get rid of strtok completely like proposed in another answer of mine

How do I return a string from a function in C?

I'm working on the following homework problem:
Given the first name and last name as parameters, write the code of
the function createFBlink(). The functions returns a facebook link
which serves as an alternate email of the facebook user. The variable
holding the facebook link should contain the minimum number of bytes
required to store the string representing the facebook link. If there
is no first name or last name, the function returns NULL.
For example, if firstname = tzuyu and lastname = chou, the
facebook link is chou.tzuyu#facebook.com.
(See the original problem statement here.)
I've been trying to return a string from createFBlink into main. I've tried multiple methods such as turning char into static but I keep getting errors that I don't understand because I don't have a lot of experience with C.
I've had the best luck with using malloc, but I've come across a problem wherein if ever there are parameters to the function I'm sending from main, I end up with a crash after the input. Here's my code so far:
#include <string.h>
#include <conio.h.>
#include <stdio.h>
#include <stdlib.h>
char *createFBlink(char *firstname , char *lastname) ;
int main(void)
{
char firstname[24] , lastname[24], fblink[24] ;
printf("Enter first name: ");
scanf("%s", firstname);
firstname[strlen(firstname)] = '\0';
printf("\n Enter last name: ");
scanf("%s", lastname);
lastname[strlen(lastname)] = '\0';
*fblink = createFBlink(firstname, lastname);
if(*firstname == '\0'){
printf("no facebook link generated");
}else{
printf("%s", *fblink);
}
getch();
return 0;
}
char * createFBlink(char *firstname , char *lastname)
{
int check1 = strlen(firstname) , check2 = strlen(lastname), num = check1+check2;
char link = (char*) malloc(sizeof(char) * num);
if(check1 == 0 || check2 == 0){
*firstname = '\0' ;
}else{
strcat(*lastname, ".");
strcat(*lastname, firstname);
strcat(*lastname, "#facebook.com");
strcpy(link , *lastname);
return link;
}
}
*link = (char *) malloc(24);
This is incorrect, it should be
link = (char *) malloc(24);
*link (the same as link[0]) is the first character of the string pointed by link, that assignment is just overwriting the character, not changing the pointer.
The following is also incorrect:
*fblink = createFBlink(firstname, lastname);
This:
strcat(*lastname, ...);
is incorrect in the same way. You are getting the first character of the string pointed by lastname, converting it to a pointer and passing this (obviously invalid) pointer to strcat. This is the most likely reason of the crash.
Also, 24 characters may not be enough to hold the concatenated string.
Try to read a book about working with pointers in C, trying to understand them via trial-and-error is probably not the most effective way.
When working with strings, you need to understand the types you are using.
This is a fixed area in memory, of fixed size.
char buffer [24];
This is a dynamically allocated buffer that must be freed
char* szBuffer = malloc(24);
free(szBuffer)
Instead of doing that correctly, your createFBlink does malloc twice, and free zero times.
If you return a malloc'ed buffer from a function, you still must free it.
char * result = createFBlink(stuff);
free(result);
Since the types are different, you would need to use another function to move the string data from one to the other.
char * result = createFBlink(stuff);
strcpy(fblink, result, sizeof(fblink));
free(result);
And then you have additional risk from writing outside the allocated space. Let's try a made-up name with some common names.
firstname "Richard"
lastname "Hernandez"
returned string "Hernandez.Richard#facebook.com"
Oh look, 31 characters in a 24 character string. We just overwrite something, somewhere on your computer. Literally anything could happen, now.
So you have all kinds of risk. You have to match malloc with free. You have to keep track of the size. All of this is considered to be VERY BAD c++ style. The std::string class is highly recommended for this. You want your string class to take care of all the resource management, so you can't mess it up while you are using it.
std::string CreateFacebookLink (const std::string &firstname, const std::string &lastname){
return firstname + "." + lastname + "#facebook.com";
}
std::string strFacebookLink (CreateFacebookLink("Richard", "Hernandez"));

split strings with strtok and then save them error

i Have the following code
char inputs []="3,0,23.30,3,30/55,55,55,55,55,55,55,55,55,55,55,55,55,64,64,64,100,100,100,100,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,55,55,70/1.5,0.5,0.2,0.2,0.3,0.1";
char parameters[18];
strcpy(parameters,strtok(inputs,"/"));
and then some code to transmit my characters through uart and see them at a monitor. when i transmit the inputs i see them fine but when i transmit the parameters i see nothing the string is empty.
i have seen examples for strtok and it uses that code to split strings. I have also tried this kind of code at visual studio and when i print them it shows me the strings fine. Is there any chance that strtok doesn't function well with a microprocessor????
While working with microcontrollers, you have to take care from which memory area you are working on. On software running on a PC, everything is stored and run from the RAM. But, on a flash microcontroller, code is run from flash (also called program memory) while data are processed from RAM (also called data memory).
In the case you are working on, the inputs variable is storing an hardcoded character array, which can be const, and we don't know in which area the compiler chose to put it. So, we could rewrite you small program just to make sure that all the data are stored in program data and we will use the "_P" functions to manipulate this data.
#include <avr/pgmspace.h > // to play in program space
const char inputs PROGMEM []="3,0,23.30,3,30/55,55,55,55,55,55,55,55,55,55,55,55,55,64,64,64,100,100,100,100,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,55,55,70/1.5,0.5,0.2,0.2,0.3,0.1"; // Now, we are sure this is in program memory space
char buffer[200]; // should be long enough to contain a copy of inputs
char parameters[18];
int length = strlen_P(inputs); // Should contains string length of 186 (just to debug)
strcpy_P(buffer,inputs); // Copy the PROGMEM data in data memory space
strcpy(parameters,strtok_P(buffer,"/")); // Parse the data now in data memory space
For more info on program space with avr gcc : http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
I don't use strtok much, if at all, but this appears to be the correct way to store the result of strtok() in an char[]:
const int NUM_PARAMS = 18;
const int MAX_CHARS = 64;
char parameters[NUM_PARAMS][MAX_CHARS];
char delims[] = "/";
char *result = NULL;
int count = 0;
result = strtok(inputs, delims);
while(result != NULL && count < NUM_PARAMS){
strncpy(parameters[count++], result, MAX_CHARS);
result = strtok(NULL, delims);
}
or this if you don't want to allocate unnecessary memory for smaller tokens:
const int NUM_PARAMS = 18;
char* parameters[NUM_PARAMS];
char delims[] = "/";
char *result = NULL;
int count = 0;
result = strtok(inputs, delims);
while(result != NULL && count < NUM_PARAMS){
parameters[count] = malloc(strlen(result) + 1);
strncpy(parameters[count++], result, MAX_CHARS);
result = strtok(NULL, delims);
}
parameters should now contain all of your tokens.
strtok() intentionally modifies the source string, replacing tokens with terminators as it goes. There is no reason to store the content in auxiliary storage whatsoever so long as the source string is mutable.
Splitting the string into conjoined constants (which will be assembled by the compiler, so they ultimately will be a single terminated string):
char inputs []="3,0,23.30,3,30/"
"55,55,55,55,55,55,55,55,55,55,55,55,55,64,64,64,100,100,100,100,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,55,55,70/"
"1.5,0.5,0.2,0.2,0.3,0.1";
Clearly the second and third sequences delimited by '/' are nowhere near 17 chars wide (remember, your copy needs one more place for the terminator, thus only 17 chars can legally be copied).
Unless there is a compelling reason to do otherwise, I see no reason you can't simply do this:
char *param = strtok(inputs, "/");
while (param != NULL)
{
// TODO: do something with param...
// ... then advance to next param
param = strtok(NULL, "/");
}
What you do with // TODO do something with param... is up to you. The length of the parameter can be retrieved by strlen(param), for example. You can make a copy, providing you have enough storage space as the destination (in the second and third cases, you don't provide enough storage with only an 18-char buffer in your example).
Regardless, remember, strtok() modifies the source inputs[] array. If that is not acceptable an alternative would be something like:
char *tmp_inputs = strdup(inputs);
if (tmp_inputs != NULL)
{
char *param = strtok(tmp_inputs, "/");
while (param != NULL)
{
// TODO: do something with param...
// ... then advance to next param
param = strtok(NULL, "/");
}
// done with copy of inputs. free it.
free(tmp_inputs);
}
There are considerably threading decisions to make as well, the very reason the version of strtok() that requires the caller (you) to tote around context between calls was invented. See strtok_r() for more information.
You have to initialialize parameters that way :
char parameters[18] = {'\0'};
Thus, it will be initialized with null characters instead of variable values. This is important, because strcpy will try to find a null character to identify the end of the string.
Just to make it clear...
parameters after its initialisation : [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
parameters after strtok : ['3',',','0',',','2','3','.','3','0',',','3',',','3','0',0,0,0,0]

C programming strtok issue

I am using strtok to extract 2 words from a string names[result]. I want to get the first value from the strtok and stored it into a char array named lastName and the second value into a char array named firstName. However I got an invalid initializer error for 2 lines which are indicated by the arrow when I compiled my code. How do I resolve my problem?
char *p = NULL;
p = strtok(names[result]," ");
char lastName[50] = p; <---
p = strtok(NULL, " ");
char firstName[50] = p; <---
printf("%s %s\n",firstName,lastName);
strtok gives the pointer to the tokenized string.
char lastName[50] = p; Isn't really a good thing that you are doing there. Should use strncpy() to copy the string, or if only want the pointer, then should store in another pointer.
Array initialization in C can only use a literal, not a variable. So your code is a syntax error.
You need to use the typical strcpy() function to copy the string, or some of the more safe (and modern) varities, like strlcpy() or snprintf().
You could also do the parsing and copying in one call, using sscanf(), with proper size specifiers in the formatting string to avoid the risk of buffer overflow.
You can initialize a string to the character array like char lastName[50] = "Sample";
In this case you are trying to initialize a pointer to the character array 'char lastName[50] = p;' which is not valid.
Better you can use strcpy, memcpy function to copy the string to the character array or you can assign it in another pointer.
The other answers are all correct in that copying the string data out will make this program work, but the reason strtok is so dastardly (and generally using it is considered ill-advised) is that it changes your input by inserting NULLs into the original string. If you're going to be using it anyway, you might as well advantage of this and just use the pointers that strtok is returning directly.
Of note, though, is that since the input is changed and maybe whoever passed that input into you is not expecting that, it might be better to copy the input to a separate string first before ever calling strtok on it.
Observe the output of this code to see what I mean:
int main(int argc, char *argv[]) {
char name[] = "Firstname Lastname";
printf("Name before strtok: %s\n", name);
char *first = strtok(name, " ");
char *last = strtok(NULL, " ");
printf("Token strings: first=%s last=%s\n", first, last);
printf("Name after strtok: %s\n", name);
}
Produces:
Firstname Name before strtok: Firstname Lastname
Token strings: first=Firstname last=Firstname
Name after strtok: Firstname

Structs, strtok, segmentation fault

I'm trying to make a program with structs and files. The following is just a part of my code(it;s not the entire program).
What i'm trying to do is: ask the user to write his command. eg. delete John
eg. enter John James 5000 ipad purchase.
The problem is that I want to split the command in order to save its 'args' for a struct element. That's why i used strtok. BUT I'm facing another problem in who to 'put' these on the struct.
Also it's seems quite odd to me how to 'pass' the 'args' to the struct in a safe way,as I save all enters (from user) on a binary file which maybe be reopened and rewrited so I cant use :
strcpy(catalog[0]->short_name, args[1]);
Because it's time the short name will be saved in the first element of the struct. But if the file is written what happens then? The first element exists so if i write ..[0] i will write up on it?
What should I do? Thanx in advance for any help! :D
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
char command[1500];
struct catalogue
{
char short_name[50];
char surname[50];
signed int amount;
char description[1000];
}*catalog[MAX];
int main ( int argc, char *argv[] )
{
int i,n;
char choice[3];
printf(">sort1: Print savings sorted by surname\n");
printf(">sort2: Print savings sorted by amount\n");
printf(">search+name:Print savings of each name searched\n");
printf(">delete+full_name+amount: Erase saving\n");
printf(">enter+full_name+amount+description: Enter saving \n");
printf(">quit: Update + EXIT program.\n");
printf("Choose your selection:\n>");
gets(command); //it save the whole command
/*in choice it;s saved only the first 2 letters(needed for menu choice again)*/
strncpy(choice,command,2);
choice[2]='\0';
char** args = (char**)malloc(strlen(command)*sizeof(char*));
memset(args, 0, sizeof(char*)*strlen(command));
char* temp = strtok(command, " \t");
for (n = 0; temp != NULL; ++n)
{
args[n] = strdup(temp);
temp = strtok(NULL, " \t");
printf(" %s ",args[n]);
}
strcpy(catalog[0]->short_name, args[1]); //segmentation fault
strcpy(catalog[0]->surname,args[2]);
catalog[0]->amount=atoi(args[3]); //atoi doesn't work
strcpy(catalog[0]->description,args[4]);
}
As a result, after running the program i get a Segmentation Fault...
for the line:
strcpy(catalog[0]->short_name, args[1]);
Any help? Any ideas?
You have 2 errors:
Your catalog[MAX] array holds MAX pointers to your struct catalogue, but none of them are initialized. The way to fix this is to either not declare them to be pointers, or to malloc them as needed, like in catalog[0] = (struct catalogue *)malloc(sizeof(struct catalogue));
Your args variable is bad. First, I don't think you intend to create an array of strings whose length is the length of your command string. That means if you type "sort1" you will create args[5]. That's nonsensical, because the length of your command has nothing to do with how many arguments it should have.
But assuming you really want to do that, you are creating space for the array, but not for the strings within the array. You will get a segfault eventually anyway (though the one you are getting is due to #1 above) because of that. You need to allocate space for each element in args as you use it.
The code could look like this:
for (n = 0; temp != NULL; ++n)
{
args[n] = (char *)malloc((strlen(temp) + 1) * sizeof(char));
strcpy(args[n], temp);
// and so on
}
The for loop assigns one argument at a time (args[n] = ...), but then accesses several arguments on each pass: *args[1], args[2], etc., which are uninitialized on the first pass.
The warning is due to another bug. You can't just assign a pointer to an array like that. Use strcpy() instead.
your array for catalouge is an array of pointers, not an array of objects, but those pointers aren't initialized to anything, hence the seg fault
try:
struct catalogue
{
char short_name[50];
char surname[50];
signed int amount;
char description[1000];
}catalog[MAX];
strcpy(catalog[0].short_name, args[1]); //segmentation fault
strcpy(catalog[0].surname,args[2]);
catalog[0].amount=atoi(args[3]); //atoi doesn't work
strcpy(catalog[0].description,args[4]);
Lots of problems in this code.
First of all, you're confusing the number of arguments on the input line for the number of entries in the catalog. In one context you're using n to count the number of args, but in another you're using it to index the catalog array.
You're creating memory management headaches where you don't need to. The args variable is completely unnecessary, and you're allocating the memory for it incorrectly anyway. You're basically saying, "allocate a pointer to char for every character in command, which is probably not what you want.
Lose args completely; you don't need it.
I realize this is not your entire program, but it's not clear why you're creating catalog as an array of pointer to struct catalog as opposed to just a regular array.
I'm not sure what you think you're doing on the line
*catalog[n]->short_name=*args[1];
The type of the expression catalog[n]->short_name is char[50]. In this context the array type is implicitly converted ("decays") to a pointer type, char *. Thus the type of the whole expression *catalog[n]->short_name is * (char *), or just plain char, which is an integral type. You're essentially trying to assign the value of the first character of args[1] to the first character of catalog[n]->short_name.
None of this matters anyway, because catalog[n] hasn't been initialized to point anywhere meaningful; the segfault is coming from the attempt to access the short_name member, which implicitly dereferences catalog[n], which is pointing somewhere random.
Next, you cannot use the assignment operator = to assign string data; you must use strcpy() or strncpy() to do that.
Finally, NEVER NEVER NEVER NEVER NEVER use gets(). It will introduce a point of failure in your code. It has been officially deprecated in C99 and should no longer be used. Use fgets() instead:
if (fgets(command, sizeof command, stdin) != NULL)
{
char *newline = strchr(command, '\n');
if (newline != NULL)
*newline = 0;
}
Here's the way you need to parse out the command string and assign the fields to members of the struct:
curToken = strtok(command, '\t');
if (curToken)
strncpy(catalog[n]->short_name, curToken, sizeof catalog[n]->short_name);
curToken = strtok(NULL, '\t');
if (curToken)
strncpy(catalog[n]->surname, curToken, sizeof catalog[n]->surname);
curToken = strtok(NULL, '\t');
if (curToken)
{
char *chk;
catalog[n]->amount = (int) strtol(curToken, &chk, 10);
if (!isspace(*chk) && *chk != 0)
fprintf(stderr,
"Warning: expected integer value for amount, received %s instead\n",
curToken);
}
curToken = strtok(NULL, '\t');
if (curToken)
strncpy(catalog[n]->description, curToken, sizeof catalog[n]->description);
This code assumes that catalog is still being declared as an array of pointer and that each element has been initialized to point somewhere meaningful. Otherwise change the declaration from struct catalog {...} *catalog[MAX]; to struct catalog {...} catalog[MAX] and change -> to ..

Resources