Get words (actual words) from a char array and see if it's in another char array

Hey guys, I’m trying to use the getLastCommand() from the voice recognition shield from 1sheeld(a shield that lets you use a lot of the sensors on your smartphone). It returns “A char array type data”. I’m trying to get one word from it and store it in another char array, for example named… word, then see if another char array named sentence contains word at all. If it does, it would return true. But I’m stuck on using a for loop to loop through the array. It says, ‘begin was not declared in this scope’. I looked it up and it said that I should put a (&the variable) to make it not a pointer anymore since loops can’t use pointers. But I couldn’t get that to work. Any idea what I should do? Thanks for your time in advance.

This is my code so far. I’m just trying to extract one word from a char array but I can’t yet.

String compare(char x*){/*in the future, if I resolve this problem, I want it so I can have one person say to the other something like "hi, how are you doing" and if the other person says something like "good, how are you" then the robot will remember that and say it if someone says that to it. But I want it to find 'the average' of the sentences. So it finds words that are similar and if there is enough similarity it says the response. That way if I said "how are you doing" instead of "hi, how are you doing" it would still say the respons.*/
  for(char ch : x){
    if(ch == ' ')
    break;/*if it finds a space, it will exit the for loop and then return the word it makes. The space should mean it is the beinning of another word.*/
    lastcommand += ch;/*forming the word. Also I have lastcommand declared as a string earlier in the code.*/
    }
    return lastcommand;// returns the word
  }
for(char ch : x){

Are we writing java code?

Thanks for your reply. With your advice it partially works now, thanks. Though, it only says the first 2 or three letters of the word. Any idea? Thanks.

String compare(char* x){
  String lastcommand;
  for(int i = 0; i<sizeof(x); i++){
    if(x[i] == ' ')
    break;
    lastcommand += x[i];
    }
    return lastcommand;
  }

What advice? I asked a question.

I don't think sizeof works like you want here. sizeof will return the size of the pointer (2 bytes), not the size of the string. If you want the length of the string use strlen.

I would avoid using String, but for you ...

String compare(const char* psz)
{
    String  str;
    char    ch;
    while ( ch = *psz++ )
    {
        if ( ' ' == ch )
        {
            break;
        }
        
        str += ch;
    }

    return str;
}

EDIT: I wouldn't call in 'compare' either!

Probably something more along the line of ...

const char* extract(const char* psz, String& str)
{
    char    ch;

    while ( ch = *psz++ )
    {
        if ( ' ' == ch )
        {
            break;
        }
        
        str += ch;
    }

    return psz;
}

... this way I could continue working my way thru the character buffer being processed.

Thanks! It works. I'm wondering though, why you put the & after the string in your second piece of code. I looked it up but it didn't make sense to me why you would put it there. Also, would it cause any problems in the future using strings? If so, how would I just use chars? I tried to modify your code to work with a character array instead of a string but it didn't seem to want to work. Thanks.

Remember that buffer has to point to enough space to hold the extracted word.

const char* extract(const char* psz, char* buffer)
{
  char ch;
  while ( ch = *psz++  &&  ' ' != ch ) {
    *buffer++ = ch;
  }
  *buffer = 0;
  return psz;
}

Edit: this should only show how to store the data in a string. The returned pointer problem with inputs ending with a word is still there.

With your guy's latest code, would I just put whatever value I want for the second...thing? Like if I was call the function extract, would I go like this?

extract("hello world", (right here would I put whatever I wanted?);

quentinthornton: Thanks! It works. I'm wondering though, why you put the & after the string in your second piece of code. I looked it up but it didn't make sense to me why you would put it there. Also, would it cause any problems in the future using strings? If so, how would I just use chars? I tried to modify your code to work with a character array instead of a string but it didn't seem to want to work. Thanks.

Lookup C++ referneces. It's much like a pointer but a reference MUST be initialized to something to compiler.

Think of it as a sort of autmated pointer requiring less work on the side using it.

In this case instead of allocating a String instance local to the extract function as returning a copy on the stack we simply point to the one that exists back in the routine the called extract and add the characters to it.

You look it up and if you have more questions.

You could use something like the following:

const byte  maxWordSize = 32;
char wordBuffer[maxWordSize +1];

  extract("hello world", wordBuffer);
String          strToken;
const char*     sz = "hello world";
char*           p_next;

p_next = extract("hello world", strToken);
Serial.print(strToken);         // hello

p_next = extract(p, strToken);
Serial.print(strToken);         //world!

Again String's have there faults and probably shouldn't be used but ...

I Whandall's code the buffer may overflow depending upon the size of the returns string where as the String version will expand the strToken as needed.

But this has it's own set of problems!

Sorry for the late reply, I've been trying to get this code to work. Thanks all you guys for all your help. This code is all thanks to you guys.

String wordBuffer;
int counter = 0;

void setup() {
 Serial.begin(9600);
}

void loop(){
extract("hello world", wordBuffer, 0);
Serial.println(wordBuffer);
delay(5000);
}
const char* extract(const char* psz, String& str, int num)// the integer is for which word I want to choose (0 will give the first word, 1 will give 2nd word and so on
{
    char    ch;
    String temp;
    if(num == 0){//finding first word
      while(ch = *psz++ && ch != ' ')// this is where I'm having trouble, if I don't remove the && ch != ' ' it doesn't work. Otherwise, it's fine.
      str += ch;
    }
    else{
      while(ch = *psz++)
        if ( ch == ' ' )
        {
           counter++;
           if(counter == num){
            while(ch = *psz++ && ch != ' '){// same problem here
                          Serial.println(ch);
              str += ch;
            }
            break;
           }

        }
    }
     counter = 0;
    return psz;
}

Also, as I've said earlier (I don't know if I made it clear or not) I'm trying to compare two strings. If one has maybe 80% of the words the same as the other string it will return true. So if one string was "hello yellow jello":) and the other one was "hello jello" then it would return true. Would this be good code for doing that? What I'm going to do is have another function find one word from one of the strings, see if any of them are in the other string, if it is store that somewhere, then repeat until all the words are gone from the first string. It would then see if at least 80 or so percent of them were matched. Is there a more efficient way, or should I continue this? Thanks in advance.

Only you can define something squishy like that.

If you are just matching words, and whitespace is not important, I would use strtok() on the key string to isolate the words and then use strstr() to see if they are present in the target string. Once you know how many words are hits, you can decide whether it is "equal" or not.

Sorry. :confused: I used your advice KeithRB to get this code, so, thanks. And most of it is Xtalkers code, so thanks Xtalker. Okay, I got pretty much everything working, but I’m getting the warning

C:\Users\Caiden\Documents\Arduino\sketch_feb19a\sketch_feb19a.ino: In function 'boolean areStringsSimilar(char*, char*)':

C:\Users\Caiden\Documents\Arduino\sketch_feb19a\sketch_feb19a.ino:40:31: warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]

   if(strstr(x, subStr(y, " ",1))){

I feel that I’m missing something pretty obvious. When I looked online nothing really made sense. Does anybody know whats “wrong”? Here’s Xtalkers and some of my code…

boolean areStringsSimilar(char *x, char *y){
  if(strstr(x, subStr(y, ' ',1))){// seeing if the first word in the y array is anywhere in the x array. 

    return true;
  }
  else
  return false;
}

char* subStr (char* str, char *delim, int index) {
  char *act, *sub, *ptr;
  static char copy[MAX_STRING_LEN];
  int i;

  // Since strtok consumes the first arg, make a copy
  strcpy(copy, str);

  for (i = 1, act = copy; i <= index; i++, act = NULL) {
     //Serial.print(".");
     sub = strtok_r(act, delim, &ptr);
     if (sub == NULL) break;
  }
  return sub;
}

Does anybody know whats "wrong"?

The message is a warning. You can ignore it.

If it REALLY bugs you, look at the argument types for strstr().

const char * strstr ( const char * str1, const char * str2 );
      char * strstr (       char * str1, const char * str2 );

Notice that the second argument is const, where the call you make to the function does not have a const second argument. Cast the second argument to const char *, to make the warning go away. (The implicit cast is what is generating the warning. An explicit cast requires that you understand, and accept, the risks of doing an explicit cast.)

quentinthornton:
I’m just trying to extract one word from a char array but I can’t yet.

Check out this sketch. It has a “locate” function which may help you. It’s a complete demo that you can load and compile to see how it works, but all you need is “locate()”. Look at the demo to see how it works.

Hope this helps.

char buffer [128];

// hexdump - just for the demo
int hexdump (const char *str)
{
    int x, len;
    len = (strlen (str) + 1);
    for (x = 0; x < len; x++) {
        sprintf (buffer, "%s", (x % 256) ? "" : "\r\n\r\n      +0 +1 +2 +3 +4 +5 +6 +7  +8 +9 +A +B +C +D +E +F");
        Serial.print (buffer);
        sprintf (buffer, "%s", (x % 8)   ? "" : " ");
        Serial.print (buffer);
        (x % 16) ? sprintf (buffer, "%02X ", str[x]) : sprintf (buffer, "\r\n%04X  %02X ", x, str[x]);
        Serial.print (buffer);
    }
    Serial.print ("\r\n");
    return x;
}

// this is what finds one string inside another - the rest is just for demo
int locate (const char *str, const char *find)
{
    int siz = strlen (str);
    int len = (strlen (find) - 1); // normalize to zero
    int x = 0;
    int y = 0;

    for (x = 0; x < siz; x++) {
        if (str[x] == find[y]) {
            if (y == len) {
                return (x - len);
            } else {
                y++;
            }
        } else {
            y = 0;
        }
    }
    return -1;
}

void setup (void)
{
    int result;

    Serial.begin (115200);

    const char *source_string = "Now is the time for all good men to come to the aid of the party";
    const char *will_find = "all good";
    const char *wont_find = "marys lamb";

    sprintf (buffer, "\r\nTest string: \"%s\"\r\n", source_string);
    Serial.print (buffer);

    hexdump (source_string);

    sprintf (buffer, "\r\nSearch for: \"%s\"\r\n", will_find);
    Serial.print (buffer);

    result = locate (source_string, will_find);

    if (result == -1) {
        sprintf (buffer, "\r\nFAILED\r\n");
    } else {
        sprintf (buffer, "\r\nFOUND at offset %d (0x%02X)\r\n", result, result);
    }
    Serial.print (buffer);

    sprintf (buffer, "\r\nSearch for: \"%s\"\r\n", wont_find);
    Serial.print (buffer);

    result = locate (source_string, wont_find);

    if (result == -1) {
        sprintf (buffer, "\r\nFAILED\r\n");
    } else {
        sprintf (buffer, "\r\nFOUND at offset %d (0x%02X)\r\n", result, result);
    }
    Serial.print (buffer);

}

void loop (void)
{
    // nothing
}

PaulS: The message is a warning. You can ignore it.

If it REALLY bugs you, look at the argument types for strstr().

const char * strstr ( const char * str1, const char * str2 );
      char * strstr (       char * str1, const char * str2 );

Notice that the second argument is const, where the call you make to the function does not have a const second argument. Cast the second argument to const char *, to make the warning go away. (The implicit cast is what is generating the warning. An explicit cast requires that you understand, and accept, the risks of doing an explicit cast.)

Could you be a little more explicit? :)

Thanks for all your guy’s help! This is the finished code.

#include <string.h>
#include <OneSheeld.h>

#define MAX_STRING_LEN  40
#define INCLUDE_VOICE_RECOGNIZER_SHIELD
#define INCLUDE_TEXT_TO_SPEECH_SHIELD
#define CUSTOM_SETTINGS

void setup(){
  OneSheeld.begin();
}
void loop(){
  char firstcommand []= "hi, how are you";
  char secondcommand []= "how are you";
  if(VoiceRecognition.isNewCommandReceived()){
  if(areStringsSimilar(firstcommand, VoiceRecognition.getLastCommand())){
    TextToSpeech.say("Similar");
  }
  else
  TextToSpeech.say("not- similar");
  delay(2000);
  }
}

int areStringsSimilar(const char* x, const char* y){
  float percentage = 0.0;
  int wordsInY = howManyWords(y);
  for (int i = 1; i<= wordsInY; i++){
  if(strstr(x, subStr(y, " ",i))){
    percentage++;
  }
  }
  if(percentage/(float)wordsInY* 100 >= 60){//find percentage of words matched. If it's more than 60% return true
    return true;
  }
  return false;
}

//gets 1 word from a string and returns it (word depends on what you put for the index)
const char* subStr (const char* str, char *delim, int index) {
  char *act, *sub, *ptr;
  static char copy[MAX_STRING_LEN];
  int i;

  // Since strtok consumes the first arg, make a copy
  strcpy(copy, str);

  for (i = 1, act = copy; i <= index; i++, act = NULL) {
     //Serial.print(".");
     sub = strtok_r(act, delim, &ptr);
     if (sub == NULL) break;
  }
  return sub;
}

//returns how many words in a string
int howManyWords(const char* y){
  int counter = 1;
  char ch;
  while(ch = *y++){
    if(ch == ' '){
      counter++;
    }
  }
  return counter;
}