I'm using the following code to split a char array:
char* Split(char* e, int index) {
index = index -1;
char* v[index +2];
char *p;
int i = 0;
p = strtok(e, ",");
while(p && i < index +2)
{
v[i] = p;
p = strtok(NULL, ",");
i++;
};
// Serial.println(v[0]);
//Serial.println(v[1]);
// Serial.println(v[2]);
return v[index];
};
I'm calling the function like this:
char array[]="1,3,4,55,6,7,66";
Serial.println("array:");
Serial.println(array);
char *out;
out = Split(array,2);
Serial.println("out:");
Serial.println(out);
Serial.println("array:");
Serial.println(array);
out = Split(array,2);
Serial.println("out:");
Serial.println(out);
The first time I call the function, everythin is fine. The result I get is "3" , and that is what I expect.
But with the second call of the function, things goes crazy, and I get just some hieroglyphics.
When I check the variables with the Serial output, I can see that "array" is the second time just "1", and this might be the reason of the curious output of the function.
But I don't understand how the first call of the function can affect the value of "array", because this variable is not touched in the function.
Can anybody help me with clarifying this issue?
The output of the serial interface is lke this:
array:
1,3,4,55,6,7,66
out:
3
array:
1
out:
⸮}⸮a⸮⸮-:⸮⸮⸮m⸮⸮⸮⸮⸮⸮⸮]⸮ʻ⸮T⸮;⸮⸮⸮N}⸮⸮⸮⸮{R⸮U)⸮⸮⸮[G⸮⸮`j⸮⸮⸮⸮⸮v⸮⸮wz⸮⸮s⸮⸮⸮⸮⸮⸮}⸮⸮2⸮⸮vz~⸮⸮⸮⸮O}⸮⸮⸮/⸮⸮nv⸮⸮^j⸮yO⸮7{⸮⸮⸮⸮z⸮Z⸮⸮⸮⸮⸮⸮⸮7[⸮⸮⸮j⸮w⸮⸮⸮⸮⸮⸮⸮w)⸮⸮c⸮⸮}⸮⸮⸮⸮⸮⸮⸮⸮⸮v⸮⸮⸮m/V⸮ys<⸮⸮ٿ⸮⸮⸮׆⸮+>ֻ⸮z6⸮=⸮D⸮⸮⸮⸮~⸮⸮⸮⸮e⸮⸮?⸮=⸮⸮W⸮⸮⸮⸮}⸮e⸮ߣN绮⸮w⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮w⸮⸮⸮⸮?⸮⸮⸮⸮⸮⸮⸮Y⸮⸮f⸮v⸮⸮u⸮p?⸮⸮^h⸮⸮}⸮⸮ݼ⸮^Wo⸮⸮⸮⸮⸮_⸮⸮⸮⸮⸮⸮;s⸮⸮⸮⸮wZ⸮⸮⸮~⸮7⸮⸮⸮r⸮⸮⸮⸮)⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮f⸮⸮O⸮⸮⸮⸮⸮⸮⸮⸮⸮
⸮⸮7⸮⸮a.⸮⸮.kG⸮⸮8⸮⸮⸮⸮⸮⸮⸮⸮U⸮⸮⸮⸮⸮⸮⸮⸮⸮'⸮we⸮⸮⸮M⸮{⸮⸮Lu⸮no⸮⸮⸮>⸮⸮⸮⸮⸮⸮⸮~}⸮⸮⸮⸮⸮⸮⸮⸮y⸮⸮o⸮⸮⸮,'>}⸮⸮⸮+⸮X⸮⸮⸮/⸮⸮ױ⸮⸮⸮⸮̲⸮⸮-_M⸮⸮⸮⸮L~⸮#Φz~⸮⸮⸮⸮?⸮⸮⸮⸮⸮{/⸮_⸮:⸮jmc⸮m]S⸮_3⸮>o⸮⸮ݸv⸮⸮⸮|⸮
⸮⸮{_^⸮⸮o⸮?⸮⸮⸮⸮⸮⸮_⸮⸮⸮⸮{⸮⸮⸮^⸮⸮⸮⸮⸮⸮⸮⸮⸮ퟺ⸮⸮߿⸮⸮p⸮⸮⸮w?=⸮⸮⸮X⸮⸮⸮⸮⸮⸮_⸮oy⸮⸮M⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮w⸮⸮⸮[⸮⸮o⸮⸮⸮⸮7wE~⸮⸮⸮⸮⸮N⸮⸮o⸮x⸮=v/⸮⸮⸮⸮>⸮9⸮⸮ί⸮Y_Q⸮⸮l⸮⸮}'⸮⸮}⸮?⸮⸮ޭ⸮6⸮7⸮{⸮T⸮⸮⸮ ⸮r⸮⸮⸮⸮⸮⸮⸮
ܽ+'⸮⸮⸮⸮G⸮f⸮z⸮Gn⸮⸮n⸮/⸮⸮⸮⸮/⸮⸮⸮⸮Q⸮⸮⸮⸮⸮o⸮;⸮L⸮⸮r⸮⸮⸮⸮n/߿ſ⸮⸮⸮⸮q⸮⸮⸮ݮ⸮⸮⸮⸮⸮⸮+⸮⸮⸮⸮⸮⸮ﷹln?⸮⸮⸮⸮q⸮⸮⸮{⸮⸮⸮⸮⸮⸮⸮q⸮-⸮⸮{(⸮⸮f⸮⸮{⸮v⸮܀⸮oq⸮⸮⸮⸮⸮⸮߽⸮⸮nj⸮⸮⸮os⸮6۟g⸮⸮⸮⸮"⸮⸮7Z7⸮⸮yo⸮ӟ⸮⸮⸮w⸮⸮⸮⸮⸮⸮{⸮⸮⸮Vr⸮⸮]_⸮SS⸮_⸮w⸮⸮⸮wl⸮⸮⸮⸮P⸮⸮z⸮⸮m{⸮⸮⸮ݛs⸮(⸮⸮r⸮⸮˷:⸮%⸮⸮⸮⸮Z⸮⸮⸮m⸮W⸮⸮⸮⸮ם*⸮⸮⸮[>⸮⸮⸮/⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮?⸮⸮⸮⸮|⸮.⸮⸮⸮{⸮⸮⸮ꍥ⸮⸮⸮|⸮⸮⸮⸮⸮⸮⸮⸮⸮7~Ls⸮!⸮⸮⸮⸮⸮{⸮x⸮g⸮⸮|֍om~~⸮⸮⸮{⸮cϠ⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮>;⸮W⸮⸮⸮⸮⸮⸮⸮⸮q⸮⸮[:⸮'⸮#⸮o⸮⸮_⸮uϾ⸮⸮⸮⸮[⸮⸮^⸮⸮⸮n⸮}⸮⸮⸮⸮⸮>/⸮_⸮⸮-⸮⸮⸮⸮s⸮⸮⸮}⸮⸮⸮⸮/⸮[w⸮⸮r⸮⸮_⸮⸮⸮⸮,⸮⸮ݯ⸮⸮⸮7ÿ⸮:⸮⸮⸮⸮Ί⸮⸮⸮⸮⸮t[|⸮⸮w⸮F⸮⸮⸮
Has anybody a better idea?
The solution depend on your data, if your data is always like a string with list of integers separated with ,, then it will probably better to split it into an int array, you can then write a generic split() function.
#define NUMBER_OF_ELEMENT 7
int splitted[NUMBER_OF_ELEMENT];
const char* delimiter = ",";
void split(const char *str, const char *delimiter) {
char temp[strlen(str)+1] = {0};
memcpy(temp, str, strlen(str));
int i=0;
char *p = strtok(temp, delimiter);
while(p != NULL) {
splitted[i++] = atoi(p);
p = strtok(NULL, delimiter);
}
}
int setup() {
char array[]="1,3,4,55,6,7,66";
Serial.begin(115200);
split(array, delimiter);
// get every splitted element as an int
for (int i=0; i<NUMBER_OF_ELEMENT, i++) {
Serial.print("Out:");
Serial.println(splitted[i]);
}
//if you really want to have string as result
Serial.println(String(splitted[3]));
}
OK I got it by making the array independent from change by strtok:
void setup(){
Serial.begin(115200);
char array[]="1,3,4,55,6,7,66";
char *out;
char array2[8];
strcpy(array2,array);
Serial.println("array2:");
Serial.println(array2);
out = Split(array2,2);
Serial.println("out:");
Serial.println(out);
Serial.println("array3:");
char array3[8];
strcpy(array3,array);
Serial.println(array3);
out = Split(array3,2);
Serial.println("out:");
Serial.println(out);
}
Not that elegant, but it works!!
Has anybody a better idea?
Thanks,great!
Yes my data is always int. So your function is perfect for me.
But for the sake of a clear structure, I would prefer to work with a return value, instead of changing the global variable "splitted".
Is that a good practice? Or do you prefer your variant for saving RAM?
Here my final solution for this task. The function just returns a single value, according to the given index:
int splitStrToInt(char *str, int index) {
//******************
//This function splits a string that is separated by commas and converts the values to int,
//according to the given index
//caution!! The string may only consist of numerical values !!
//Example: "1,3,4,55,6,7,66"
//******************
index--;
char e[strlen(str) + 1] = { 0 };
memcpy(e, str, strlen(str));
int v[index + 2];
char *p;
int i = 0;
p = strtok(e, ",");
while (p && i < index + 2) {
v[i] = atoi(p);
p = strtok(NULL, ",");
i++;
}
;
return v[index];
}
Related
I basically want to test if char *tk[2]; has some value or not stored .
My programs has to do something when tk[0] and tk[1] both have values(a char stored in each of them) , but also do something else when only tk[0] is initialized with something and tk[1] is "empty".
This is my function where i initialize my array of pointers , that uses strtok.
The keywords can be something like "key1 key2" or "key1",so maximum 2 words.
void key(char *keywords[], char *tk[], int k) {
int p = 0;
char *delim = " \n";
int length = strlen(keywords[k]);
char buffer_key[length];
strcpy(buffer_key, keywords[k]);
char *token_key = strtok(buffer_key, delim);
while (token_key != NULL) {
tk[p++] = token_key;
token_key = strtok(NULL, delim);
}
}
and this is the block of code from my main function where i call the key function .
char *tk[2];
key(keywords, tk, k);
Next i want to check if only tk[0] has a char stored (so tk[1] is empty?).
How can i check this ?
You should initialize tk before calling the function. Then you can test if the values are filled in after it returns.
char *tk[2] = {0};
key(keywords, tk, k);
if (tk[0] && !tk[1]) {
// do something
}
I got the following string from the user:
char *abc = "a234bc567d";
but all the numbers can have different lengths than in this example (letters are constants).
How can I get each part of numbers? (again, it can be 234 or 23743 or something else..)
I tried to use strchr and strncpy but I need to allocate memory for this (for strncpy), and I hope there is a better solution.
Thanks.
You can do something like this:
char *abc = "a234bc567d";
char *ptr = abc; // point to start of abc
// While not at the end of the string
while (*ptr != '\0')
{
// If position is the start of a number
if (isdigit(*ptr))
{
// Get value (assuming base 10), store end position of number in ptr
int value = strtol(ptr, &ptr, 10);
printf("Found value %d\n", value);
}
else
{
ptr++; // Increase pointer
}
}
If I understand your question, you are trying to extract the parts of the user input that contain numbers ... and the sequence of numbers can be variable ... but the letters are fixed i.e. a or b or c or d. Correct ... ? The following program may help you. I tried it for strings "a234bc567d", "a23743bc567d" and "a23743bc5672344d". Works ...
int main()
{
char *sUser = "a234bc567d";
//char *sUser = "a23743bc567d";
//char *sUser = "a23743bc5672344d";
int iLen = strlen(sUser);
char *sInput = (char *)malloc((iLen+1) * sizeof(char));
strcpy(sInput, sUser);
char *sSeparator = "abcd";
char *pToken = strtok(sInput, sSeparator);
while(1)
{
if(pToken == NULL)
break;
printf("Token = %s\n", pToken);
pToken = strtok(NULL, sSeparator);
}
return 0;
}
Here is my code
//Split up the config by lines
int x;
int numberOfConfigLines = 0;
for (x = 0; x < strlen(buffer); x++)
{
if (buffer[x] == '\n') {
numberOfConfigLines++;
}
}
char *configLines[numberOfConfigLines];
tokenize(configLines, buffer, "\n", numberOfConfigLines);
The idea of this function is to count the amount of newlines in a buffer, then split the buffer into a strtok array using this:
#include <string.h>
#include <stdlib.h>
void tokenize(char **arrToStoreTokens, char *delimitedString, char *delimiter, int expectedTokenArraySize) {
//Create a clone of the original string to prevent making permanent changes to it
char *tempString = (char *)malloc(strlen(delimitedString) + 1);
strcpy(tempString, delimitedString);
if (expectedTokenArraySize >= 1) {
arrToStoreTokens[0] = strtok(tempString, delimiter);
int x;
for (x = 1; x < expectedTokenArraySize; x++ ) {
arrToStoreTokens[x] = strtok(NULL, delimiter);
}
}
//Dispose of temporary clone
free(tempString);
}
If I access arrToStoreTokens[0] directly, I get the correct result, however when I try to access configLines[0] once thetokenize function has ended, I get different results (can be unknown characters or simply empty)
Additionally, I believe this has only started occurring once I began running the program as root (for a different requirement) - I may be wrong though. - EDIT: Confirmed not to be the problem.
Any ideas?
strtok doesn't reallocate anything. It only makes cut and pointers of what you gave to it.
Your array stores pointers that strtok gives you, but don't copy contents.
So if you free your tempString variable, you free data that was pointed by return values of strtok. You have to keep it and free it only at the end.
Or you can make a strdup of each return of strtok to store it in your array to make a real copy of each token, but in this case, you shall have to free each token at the end.
The second solution would looks like this :
void tokenize(char **arrToStoreTokens, char *delimitedString, char *delimiter, int expectedTokenArraySize) {
//Create a clone of the original string to prevent making permanent changes to it
char *tempString = (char *)malloc(strlen(delimitedString) + 1);
strcpy(tempString, delimitedString);
if (expectedTokenArraySize >= 1) {
arrToStoreTokens[0] = strdup(strtok(tempString, delimiter)); // Here is the new part : strdup
int x;
for (x = 1; x < expectedTokenArraySize; x++ ) {
arrToStoreTokens[x] = strdup(strtok(NULL, delimiter)); // Here is the new part : strdup
}
}
//Dispose of temporary clone
free(tempString);
}
And after use of this array, you shall have to delete it, with a function like this :
void deleteTokens(char **arrToStoreTokens, int arraySize)
{
int x;
for (x = 0; x < arraySize; ++x)
{
free(arrToStoreTokens[x]);
}
}
I need to extract a value for a given key from a string. I made this quick attempt:
char js[] = "some preceding text with\n"
"new lines and spaces\n"
"param_1=123\n"
"param_2=321\n"
"param_3=string\n"
"param_2=321\n";
char* param_name = "param_2";
char *key_s, *val_s;
char buf[32];
key_s = strstr(js, param_name);
if (key_s == NULL)
return 0;
val_s = strchr(key_s, '=');
if (val_s == NULL)
return 0;
sscanf(val_s + 1, "%31s", buf);
printf("'%s'\n", buf);
And it in fact works ok (printf gives '321'). But I suppose the scanf/sscanf would make this task even easier but I have not managed to figure out the formatting string for that.
Is that possible to pass a content of a variable param_name into sscanf so that it evaluates it as a part of a formatting string? In other words, I need to instruct sscanf that in this case it should look for a pattern param_2=%s (the param_name in fact comes from a function argument).
Not directly, no.
In practice, there's of course nothing stopping you from building the format string for sscanf() at runtime, with e.g. snprintf().
Something like:
void print_value(const char **js, size_t num_js, const char *key)
{
char tmp[32], value[32];
snprintf(tmp, sizeof tmp, "%s=%%31s", key);
for(size_t i = 0; i < num_js; ++i)
{
if(sscanf(js[i], tmp, value) == 1)
{
printf("found '%s'\n", value);
break;
}
}
}
OP's has a good first step:
char *key_s = strstr(js, param_name);
if (key_s == NULL)
return 0;
The rest may be simplified to
if (sscanf(&key_s[strlen(param_name)], "=%31s", buf) == 0) {
return 0;
}
printf("'%s'\n", buf);
Alternatively one could use " =%31s" to allow spaces before =.
OP's approach gets fooled by "param_2 321\n" "param_3=string\n".
Note: Weakness to all answers so far to not parse the empty string.
One issue that bears consideration is the difference between finding a 'key=value' setting in the string for a specific key value (such as param_2 in the question), and finding any 'key=value' setting in the string (with no specific key in mind a priori). The techniques to be used are rather different.
Another issue that has not self-evidently been considered is the possibility that you're looking for a key param_2 but the string also contains param_22=xyz and t_param_2=abc. The simple-minded approaches using strstr() to hunt for param_2 will pick up either of those alternatives.
In the sample data, there is a collection of characters that are not in the 'key=value' format to be skipped before the any 'key=value' parts. In the general case, we should assume that such data appears before, in between, and after the 'key=value' pairs. It appears that the values do not need to support complications such as quoted strings and metacharacters, and the value is delimited by white space. There is no comment convention visible.
Here's some workable code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAX_KEY_LEN = 31 };
enum { MAX_VAL_LEN = 63 };
int find_any_key_value(const char *str, char *key, char *value);
int find_key_value(const char *str, const char *key, char *value);
int find_any_key_value(const char *str, char *key, char *value)
{
char junk[256];
const char *search = str;
while (*search != '\0')
{
int offset;
if (sscanf(search, " %31[a-zA-Z_0-9]=%63s%n", key, value, &offset) == 2)
return(search + offset - str);
int rc;
if ((rc = sscanf(search, "%255s%n", junk, &offset)) != 1)
return EOF;
search += offset;
}
return EOF;
}
int find_key_value(const char *str, const char *key, char *value)
{
char found[MAX_KEY_LEN + 1];
int offset;
const char *search = str;
while ((offset = find_any_key_value(search, found, value)) > 0)
{
if (strcmp(found, key) == 0)
return(search + offset - str);
search += offset;
}
return offset;
}
int main(void)
{
char js[] = "some preceding text with\n"
"new lines and spaces\n"
"param_1=123\n"
"param_2=321\n"
"param_3=string\n"
"param_4=param_2=confusion\n"
"m= x\n"
"param_2=987\n";
const char p2_key[] = "param_2";
int offset;
const char *str;
char key[MAX_KEY_LEN + 1];
char value[MAX_VAL_LEN + 1];
printf("String being scanned is:\n[[%s]]\n", js);
str = js;
while ((offset = find_any_key_value(str, key, value)) > 0)
{
printf("Any found key = [%s] value = [%s]\n", key, value);
str += offset;
}
str = js;
while ((offset = find_key_value(str, p2_key, value)) > 0)
{
printf("Found key %s with value = [%s]\n", p2_key, value);
str += offset;
}
return 0;
}
Sample output:
$ ./so24490410
String being scanned is:
[[some preceding text with
new lines and spaces
param_1=123
param_2=321
param_3=string
param_4=param_2=confusion
m= x
param_2=987
]]
Any found key = [param_1] value = [123]
Any found key = [param_2] value = [321]
Any found key = [param_3] value = [string]
Any found key = [param_4] value = [param_2=confusion]
Any found key = [m] value = [x]
Any found key = [param_2] value = [987]
Found key param_2 with value = [321]
Found key param_2 with value = [987]
$
If you need to handle different key or value lengths, you need to adjust the format strings as well as the enumerations. If you pass the size of the key buffer and the size of the value buffer to the functions, then you need to use snprint() to create the format strings used by sscanf(). There is an outside chance that you might have a single 'word' of 255 characters followed immediately by the target 'key=value' string. The chances are ridiculously small, but you might decide you need to worry about that (it prevents this code being bomb-proof).
I'm having a hard time understanding how should I do the following:
I have a list of words defined like so:
typedef struct _StringNode {
char *str;
struct _StringNode* next;
} StringNode;
Now I need to write a function which receives a string, and two word lists of the same length, and I need to replace every appearance of a word from the first list in the string with the corresponding word from the second list.
Example:
text: "stack overflow siteoverflow oveflow stack"
patterns: [ "stack", "overflow", "site" ]
replacements: [ "Hello", "guys", "here" ]
result: "Hello guys hereguys guys Hello"
For each word: I'm trying to use strstr() so I'll get a pointer to an occurrence of the word in a copy of the string and then to change the word, and to promote the pointer of the copy of the text string.
char* replace(const char *text,
const StringNode *patterns,
const StringNode *replacements);
You can use this
char *strnreplace(char *st,const int length,
const char *orig,const char *repl) {
static char buffer[length];
char *ch;
if (!(ch = strstr(st, orig)))
return st;
strncpy(buffer, st, ch-st);
buffer[ch-st] = 0;
sprintf(buffer+(ch-st), "%s%s", repl, ch+strlen(orig));
return buffer;
}
void replace(const char *text,
const StringNode *patterns,
const StringNode *replacements)
{
StringNode *pat, *rep;
char *temp = text;
int length = strlen(text);
for( pat = patterns, rep = replacements;
pat->next != NULL;
pat = pat->next, rep = rep->next ) {
temp = strnreplace(temp, length, pat->str, rep->str);
}
}
Perhaps something like this:
char* replace(const char *text,
const StringNode *patterns,
const StringNode *replacements)
{
char *out = malloc(1024), *put = out;
while(*text != '\0)
{
const StringNode *piter, *riter;
int found = 0;
/* Check if current start of text matches any pattern. */
for(piter = patterns, riter = replacements;
piter != NULL;
piter = piter->next, riter = riter->next)
{
const size_t plen = strlen(piter->str);
if(strncmp(text, piter->str, plen) == 0)
{
/* Hit found, emit replacement. */
const size_t rlen = strlen(riter->str);
memcpy(out, riter->str, rlen);
out += rlen;
text += plen;
found = 1;
break;
}
}
if(!found)
*put++ = *text++;
}
*put = '\0';
return out;
}
Note that the above does not handle buffer overflows, omitted for brevity. I would recommend implementing something like this on top of a dynamic string data type, to make the core operation (append) automatically grow the destination string as needed.
UPDATE In response to the comment, the algorithm the above is trying to implement is:
set output to empty string
while text remaining
if start of text matches pattern[i]
append replacement[i] to output
remove len(pattern[i]) characters from start of text
else
append first character of text to output
remove first character of text
So, it repeatedly checks for pattern-matches, as long as there is anything left in text.