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;
}
}
Related
I m trying to create hash of file by reading its content, and then hashing it using SHA1, after this I m trying to create a filename with this hash value, but the issue only first two char of hash string is taken.
Hash function
void hash(const std::string& content, std::string& retValue, bool isFile)
{
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, content.c_str(), content.size());
if (isFile) {
FILE* f = fopen(content.c_str(),"r");
assert(f != NULL);
char buffer[1024]={0};
while(fgets(buffer,1024,f))
SHA1_Update(&ctx,buffer,strlen(buffer));
fclose(f);
}
unsigned char hash[SHA_DIGEST_LENGTH] = {0};
SHA1_Final(hash,&ctx);
retValue.resize(60);
int cnt = 0;
for(int i=0;i<SHA_DIGEST_LENGTH;i++){
char ch[3] = {0};
sprintf(ch,"%02x",hash[i]);
retValue[cnt++] = ch[0];
retValue[cnt++] = ch[1];
retValue[cnt++] = ch[2];
}
std::cout<<retValue<<std::endl;
}
when I print hash value in console I get a valid looking val such as this 51d946cde43e90ec03493f88914ca948b5215916, but it contains only first two characters in this case '51', I switched to gdb to view the string, gdb shows me the string in a weird way 51\000d9\000\064\066\000cd\000e4\000\063e\000\071\060\000ec\000\060\063\000\064\071\000\063f\000\070\070\000\071\061\000\064c\000a9\000\064\070\000b5\000\062\06 1\000\065\071\000\061\066. I am saving file with std::ofstream. I have looked at prev answers that said this is a bug in gdb so it prints like this, but here I can see file getting created doesnt with only first char of hash.
Why is this happening?
Thank You.
-----------------------------------------------EDIT-------------------------------------------------------------------------
From comment of dvix, it got resolved, I was copying '\0' with ch[2], so basically I was needed to only copy first two character from each hex character, ch[0] and ch[1] and then place null at very end of string.
so the code inside of for loop should look like this
for(int i=0;i<SHA_DIGEST_LENGTH;i++){
char ch[3] = {0};
sprintf(ch,"%02x",hash[i]);
retValue[cnt++] = ch[0];
retValue[cnt++] = ch[1];
}
retValue[cnt] = '\0';
Here's a slightly shorter version that does the same thing:
for(int i=0;i<SHA_DIGEST_LENGTH;i++){
sprintf(retValue + cnt,"%02x",hash[i]);
cnt += 2;
}
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.
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 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
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