I use an arduino MEGA to parse a the param part of an url.
It should not matter how the order of the parameters are. I have following code and I tried that with strtok.
char text[] = "ssid=SSID&pwd=PASSWORD&userId=1234"
and will split in
ssid=SSID
pwd=PASSWORD
userId=1234
and split it again in key and values
ssid
SSID
pwd
PASSWORD
userId
1234
I tried to use strtok for the first split.
char *ptr;
ptr = strtok(params, "&");
while (ptr != NULL) {
Serial.println(urlParam);
ptr = strtok(NULL, "&");
}
and the output is as expected:
Output:
ssid=SSID
pwd=PASSWORD
userId=1234
then the next split:
char *ptr;
ptr = strtok(params, "&");
while (ptr != NULL) {
char *paramKey;
char *paramValue;
paramKey = strtok(ptr, "=");
Serial.println(paramKey);
if (paramKey == 'ssid'){
paramValue = strtok(NULL, "=");
Serial.println(paramValue);
ssidName = paramValue;
}
if (paramKey == 'pwd'){
...
}
if (paramKey == 'userId'){
...
}
ptr = strtok(NULL, "&");
}
But the output is just
ssid
SSID
Looks like the loop is not working properly.
Where do I make the mistake?
Is there any other way to resolve this string?
The strtok function uses an internal static variable to keep track of its current state. When you use the function for multiple different substrings interleaved like you're doing, you step on the internal state.
You need to instead use strtok_r, which uses an external variable to keep track of state.
char *ptr, *sav1 = NULL;
ptr = strtok_r(params, "&", &sav1); // outer strtok_r, use sav1
while (ptr != NULL) {
char *paramKey;
char *paramValue;
char *sav2 = NULL;
paramKey = strtok_r(ptr, "=", &sav2); // inner strtok_r, use sav2
Serial.println(paramKey);
if (!strcmp(paramKey, "ssid")) {
paramValue = strtok_r(NULL, "=", &sav2); // inner strtok_r, use sav2
Serial.println(paramValue);
ssidName = paramValue;
}
if (!strcmp(paramKey, "pwd")) {
...
}
if (!strcmp(paramKey, "userId")) {
...
}
ptr = strtok_r(NULL, "&", &sav1); // outer strtok_r, use sav1
}
Not related to the parsing issue, you also can't compare strings with ==. You need to use strcmp instead, and string constants are surrounded in double quotes, not single quotes.
You have three problems:
The first is that you use implementation-specific multi-character literals like e.g. 'ssid', when you should be using strings like "ssid".
The second problem is that you use == to compare strings. That's almost impossible to get to work, as then you compare the pointers instead of the string contents. To compare strings you need to use strcmp.
The strtok function is not reentrant. You can't have multiple strtok parsings going simultaneously. Either separate the steps, or use strtok_s (or strtok_r if such a function is available).
#include <stdio.h>
#include <string.h>
struct split_result {
char first_part[64];
char second_part[64];
};
split_result return_left(char splitter, const char *str);
int main() {
/*
ssid=SSID
pwd=PASSWORD
userId=1234
*/
char *str = "ssid=109304905995";
split_result result = return_left('=', str);
printf("first part: %s \n second part: %s\n", result.first_part, result.second_part);
return 0;
}
split_result return_left(char splitter, const char *str) {
int i = 0;
split_result split_ptr;
while (str[i] != 0) {
//we found splitter
if ((char)str[i] == splitter) {
//remove first half
strncpy_s(split_ptr.first_part, str, (int)(&str[i]-str));
break;
}
//we need to remove second half now
i++;
}
strcpy_s(split_ptr.second_part, &str[i+1]);
return split_ptr;
}
I hope this will be helpful to anyone.
Related
I'm trying to get my head around how to split arrays and use the tokens in an if statement, however I'm not having much luck.
The below code is for an Arduino. What I am doing is passing the function receviedChars which will be something like:
token0,token1,token2
When i print out func, it reads out c, so I figured that if I compared func to c it should match true. Unfortunately, this doesn't seem to happen.
I'm quite new to C++ and Arduino, and mainly have a web development background so I might be misinterpreting something
const byte numChars = 32;
char receivedChars[numChars];
char *chars_array = strtok(receivedChars, ",");
char *func = chars_array;
Serial.println(func);
if(func == 'c') {
Serial.println("It works");
}
Could someone help me with where I am going wrong please?
First of all, strtok works iteratively. This means that to split a string into tokens you have to call it until it returns NULL:
char* token = strtok(input, ",");
while (token)
{
...
token = strtok(NULL, ",");
}
And the second thing to know is that char * is just a pointer to a block of memory treated as a string. So when you write something like:
char* str = ...;
if (str == 'c')
{
...
}
This actually means "compare an address pointed by variable 'str' with a value of an ASCII code of character 'c' (which is 0x63 in hex)", therefore your condition will be true iff the pointer returned by strtok equals to 0x63 and that is definitely not what you want.
What you really need is strcmp function, that compares two blocks of memory character by character:
char* chars_array = strtok(receivedChars, ",");
if (strcmp(chars_array, "bla") == 0)
{
// a first token is "bla"
}
Swap
if(func == 'c') {
to
if(func[0] == 'c') {
if you want to check if first char is 'c'
'func' is a pointer to the start of an array of characters; comparing it to a character value will almost never yield true. Perhaps you want to compare the character in that array instead.
The main issue is that you should use if(*func == 'c') {, i.e. dereference pointer func, instead of if(func == 'c') {.
Note that you additionally should consider that chars_array might be an empty string or might comprise only ','-characters; in this case, strtok will yield NULL, and probably lets your app crash. Hence, the code should look as follows:
if (func != nullptr) {
Serial.println(func);
if(*func == 'c') {
Serial.println("It works");
}
}
I am trying to parse a string of format something like 1-3,5-7. I need to read 1,3 and 5,7.
What I am doing
char *dup_string;
dup_string = strdup(data);
tok = strtok(dup_string, ",");
while (tok != NULL)
{
char *rangeTok;
rangeTok = (char *)memchr(tok, "-", strlen(tok));
startpage = atoi(tok);
if(rangeTok != NULL)
{
*rangeTok++;
endpage = atoi(rangeTok);
}
else
endpage = startpage;
tok = strtok(NULL,",");
}
Here memchar returning a badptr, I have tried using strchr which is also returning batptr. Any ideas why it is returning badptr.
FYI, earlier I tried:
tok = strchr(dupstring, ",");
which worked fine for sometime, and started returning badptr. I am not sure why it is doing that.
You're passing the wrong argument to both strchr and memchr, as has already been pointed out. The second argument is an integer holding the value of a character, not a const char *.
This line
rangeTok = (char *)memchr(tok, "-", strlen(tok));
should be either
rangeTok = (char *)memchr(tok, '-', strlen(tok));
or preferably
rangeTok = strchr(tok, '-');
As an aside, what is this badptr? Do you just mean NULL?
The prototype of memchr() is as follows, void * memchr(void * ptr, int value, size_t num);. But you are passing a string in memchr(tok, "-", strlen(tok)); instead of an integer. The way you used strtok() is also wrong, It should be as follows,
tok = strtok(dup_string, ",");
while (tok != NULL)
{
/* Body of Loop */
tok = strtok(NULL,",");
}
On a first call, strtok() expects a string as the first argument, 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 last token as the new starting location for scanning.
try to use sscanf() in this way
#include<stdio.h>
#include<string.h>
main()
{
char *data = "1-3,5,8-9";
char *ptr = data;
int e, pos=0, startpage, endpage;
while((e=sscanf(ptr, "%d-%d%n", &startpage, &endpage, &pos))>=1)
{
ptr+=pos;
if(e==1)
endpage = startpage;
printf("start page %d ** end page %d\n",startpage,endpage);
if (sscanf(ptr, " %*[,]%n", &pos) >= 0)
ptr+=pos;
}
}
I need to be able to parse the following two strings in my program:
cat myfile || sort
more myfile || grep DeKalb
The string is being saved in char buffer[1024]. What I need to end up with is a pointer to a char array for the left side, and a pointer to a char array for the right side so that I can use these to call the following for each side:
int execvp(const char *file, char *const argv[]);
Anyone have any ideas as to how I can get the right arguments for the execvp command if the two strings above are saved in a character buffer char buffer[1024]; ?
I need char *left to hold the first word of the left side, then char *const leftArgv[] to hold both words on the left side. Then I need the same thing for the right. I have been messing around with strtok for like two hours now and I am hitting a wall. Anyone have any ideas?
I recommend you to learn more about regular expressions. And in order to solve your problem painlessly, you could utilize the Boost.Regex library which provides a powerful regular expression engine. The solution would be just several lines of code, but I encourage you to do it yourself - that would be a good exercise. If you still have problems, come back with some results and clearly state where you were stuck.
You could use std::getline(stream, stringToReadInto, delimeter).
I personally use my own function, which has some addition features baked into it, that looks like this:
StringList Seperate(const std::string &str, char divider, SeperationFlags seperationFlags, CharValidatorFunc whitespaceFunc)
{
return Seperate(str, CV_IS(divider), seperationFlags, whitespaceFunc);
}
StringList Seperate(const std::string &str, CharValidatorFunc isDividerFunc, SeperationFlags seperationFlags, CharValidatorFunc whitespaceFunc)
{
bool keepEmptySegments = (seperationFlags & String::KeepEmptySegments);
bool keepWhitespacePadding = (seperationFlags & String::KeepWhitespacePadding);
StringList stringList;
size_t startOfSegment = 0;
for(size_t pos = 0; pos < str.size(); pos++)
{
if(isDividerFunc(str[pos]))
{
//Grab the past segment.
std::string segment = str.substr(startOfSegment, (pos - startOfSegment));
if(!keepWhitespacePadding)
{
segment = String::RemovePadding(segment);
}
if(keepEmptySegments || !segment.empty())
{
stringList.push_back(segment);
}
//If we aren't keeping empty segments, speedily check for multiple seperators in a row.
if(!keepEmptySegments)
{
//Keep looping until we don't find a divider.
do
{
//Increment and mark this as the (potential) beginning of a new segment.
startOfSegment = ++pos;
//Check if we've reached the end of the string.
if(pos >= str.size())
{
break;
}
}
while(isDividerFunc(str[pos]));
}
else
{
//Mark the beginning of a new segment.
startOfSegment = (pos + 1);
}
}
}
//The final segment.
std::string lastSegment = str.substr(startOfSegment, (str.size() - startOfSegment));
if(keepEmptySegments || !lastSegment.empty())
{
stringList.push_back(lastSegment);
}
return stringList;
}
Where 'StringList' is a typedef of std::vector, and CharValidatorFunc is a function pointer (actually, std::function to allow functor and lambda support) for a function taking one char, and returning a bool. it can be used like so:
StringList results = String::Seperate(" Meow meow , Green, \t\t\nblue\n \n, Kitties!", ',' /* delimeter */, DefaultFlags, is_whitespace);
And would return the results:
{"Meow meow", "Green", "blue", "Kitties!"}
Preserving the internal whitespace of 'Meow meow', but removing the spaces and tabs and newlines surrounding the variables, and splitting upon commas.
(CV_IS is a functor object for matching a specific char or a specific collection of chars taken as a string-literal. I also have CV_AND and CV_OR for combining char validator functions)
For a string literal, I'd just toss it into a std::string() and then pass it to the function, unless extreme performance is required. Breaking on delimeters is fairly easy to roll your own - the above function is just customized to my projects' typical usage and requirements, but feel free to modify it and claim it for yourself.
In case this gives anyone else grief, this is how I solved the problem:
//variables for the input and arguments
char *command[2];
char *ptr;
char *LeftArg[3];
char *RightArg[3];
char buf[1024]; //input buffer
//parse left and right of the ||
number = 0;
command[0] = strtok(buf, "||");
//split left and right
while((ptr=strtok(NULL, "||")) != NULL)
{
number++;
command[number]=ptr;
}
//parse the spaces out of the left side
number = 0;
LeftArg[0] = strtok(command[0], " ");
//split the arguments
while((ptr=strtok(NULL, " ")) != NULL)
{
number++;
LeftArg[number]=ptr;
}
//put null at the end of the array
number++;
LeftArg[number] = NULL;
//parse the spaces out of the right side
number = 0;
RightArg[0] = strtok(command[1], " ");
//split the arguments
while((ptr=strtok(NULL, " ")) != NULL)
{
number++;
RightArg[number]=ptr;
}
//put null at the end of the array
number++;
RightArg[number] = NULL;
Now you can use LeftArg and RightArg in the command, after you get the piping right
execvp(LeftArg[0], LeftArg);//execute left side of the command
Then pipe to the right side of the command and do
execvp(RightArg[0], RightArg);//execute right side of command
well i have to process a large chunk of text, analysing it linear from begin to end. And i wonder what is the better approach for this: using char* or std::string.
while using char* i can alter the pointer to a position further in the string eg.
//EDIT later: mallocing some space for text
char str[] = "text to analyse";
char * orig = str;
//process
str += processed_chars; //quite fast
//process again
// later: free(orig);
but using string i might have to use std::string::erase - but it create a copy, or move bytes or something (i don't know the actual implementation)
string str = "text to analyse";
//process
str = str.erase(0,processed_chars);
or is there a way to alter the std::string's hidden pointer?
EDIT: as Sylvain Defresne requested here more code:
class tag {
public:
tag(char ** pch) {
*pch = strstr(*pch,"<");
if(pch == NULL) return;
char *orig = *pch+1;
*pch = strstr(*pch,">");
if(pch == NULL) return;
*pch+=sizeof(char); //moving behind the >
//process inner tag data
if(*(*pch-2)!='/'){// not selfclose
while (!(**pch == '<' && *(*pch+1) == '/')){ //sarch for closing tag
tag* kid = new tag(pch);
sublings.push_back(*kid);
}
*pch = strstr(*pch,">");
if(pch == NULL) return;
*pch+=sizeof(char); //moving behind the >
//add check if the clothing tag is matching
}
}
}
i use it for recursive xml-like notation parsing
char str[] ="<father><kid /></fatherr>";
char * pch = str;
tag *root = new tag(&pch);
this code is ugly as hell, i am just starting with low-level pointer arithmetic and stuff, used visual components till now so don't judge too hard
With std::string, you would probably use std::string::iterator. Your code would be:
std::string str = "text to analyse";
std::string::iterator iter = str.begin();
// process
iter += processed_chars;
Anything you can do with a char*, you can do with an std::string::iterator.
You can use std::string::iterator (look here).
std::string is not neccassary in such task (but such classes as std::string are very useful in other situations).
I'm getting the text from editbox and I'd want to get each name separated by enter key like the character string below with NULL characters.
char *names = "Name1\0Name2\0Name3\0Name4\0Name5";
while(*names)
{
names += strlen(names)+1;
}
how would you do the same for enter key (i.e separated by /r/n) ? can you do that without using the std::string class?
Use strstr:
while (*names)
{
char *next = strstr(names, "\r\n");
if (next != NULL)
{
// If you want to use the key, the length is
size_t len = next - names;
// do something with a string here. The string is not 0 terminated
// so you need to use only 'len' bytes. How you do this depends on
// your need.
// Have names point to the first character after the \r\n
names = next + 2;
}
else
{
// do something with name here. This version is 0 terminated
// so it's easy to use
// Have names point to the terminating \0
names += strlen(names);
}
}
One thing to note is that this code also fixes an error in your code. Your string is terminated by a single \0, so the last iteration will have names point to the first byte after your string. To fix your existing code, you need to change the value of names to:
// The algorithm needs two \0's at the end (one so the final
// strlen will work and the second so that the while loop will
// terminate). Add one explicitly and allow the compiler to
// add a second one.
char *names = "Name1\0Name2\0Name3\0Name4\0Name5\0";
If you want to start and finish with a C string, it's not really C++.
This is a job for strsep.
#include <stdlib.h>
void split_string( char *multiline ) {
do strsep( &multiline, "\r\n" );
while ( multiline );
}
Each call to strsep zeroes out either a \r or a \n. Since only the string \r\n appears, every other call will return an argument. If you wanted, you could build an array of char*s by recording multiline as it advances or the return value of strsep.
void split_string( char *multiline ) {
vector< char* > args;
do {
char *arg = strsep( &multiline, "\r\n" );
if ( * arg != 0 ) {
args.push_back( arg );
}
} while ( multiline );
}
This second example is at least not specific to Windows.
Here's a pure pointer solution
char * names = "name1\r\nName2\r\nName3";
char * plast = names;
while (*names)
{
if (names[0] == '\r' && names[1] == '\n')
{
if (plast < names)
{
size_t cch = names - plast;
// plast points to a name of length cch, not null terminated.
// to extract the name use
// strncpy(pout, plast, cch);
// pout[cch] = '\0';
}
plast = names+2;
}
++names;
}
// plast now points to the start of the last name, it is null terminated.
// extract it with
// strcpy(pout, plast);
Since this has the C++ tag, the easiest would probably using the C++ standard library, especially strings and string streams. Why do you want to avoid std::string when you're doing C++?
std::istringstream iss(names);
std::string line;
while( std::getline(iss,line) )
process(line); // do process(line.c_str()) instead if you need to