Arduino: Splitting a string AND randomly selecting tokens - string

I built a program in Processing that pseudorandomly generates imperative sentences. The program combines a randomly selected verb, possessive adjective, and noun in order to display a final sentence.
Here is a summarized version of the program:
void sentence() {
String VerbList = "abide accelerate accept accomplish achieve acquire acted etc.”;
String[] Verbs = VerbList.split("\\s");
String PossessiveAdjectiveList = "my your his her its our their";
String [] PossessiveAdjectives = PossessiveAdjectiveList.split("\\s");
String NounList = "account achiever acoustics act action activity actor etc.”;
String[] Nouns = NounList.split("\\s");
int verb = int(random(Verbs.length));
int possessiveAdjective = int(random(PossessiveAdjectives.length));
int noun = int(random(Nouns.length));
String Sentence = Verbs[verb]+" "+PossessiveAdjectives[possessiveAdjective]+" "+Nouns[noun];
println(Sentence);
Upon moving the code to the Arduino IDE, I immediately discovered the absence of a string.split function. I understand that I can turn a string into tokens using strtok; however, I am not sure how to select individual tokens through randomly generated integers. Should I be trying to use strtok? Here is my code thus far:
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
#define WHITE 0x7
void setup() {
Serial.begin(9600);
lcd.setBacklight(WHITE);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
sentence();
}
void sentence() {
char VerbList[] = "abide accelerate accept accomplish achieve acquire acted etc.";
char* Verbs = strtok(VerbList, " ");
char PossessiveAdjectiveList[] = "my your his her its our their";
char* PossessiveAdjectives = strtok(PossessiveAdjectiveList, " ");
char NounList[] = "account achiever acoustics act action activity actor etc.";
char* Nouns = strtok(NounList, " ");
//int verb = int(random(Verbs.length));
//int verb = Verbs.substring(random(Verbs.length));
//int possessiveAdjective = int(random(PossessiveAdjectives.length));
//int noun = int(random(Nouns.length));
//String Sentence = Verbs[verb]+" "+PossessiveAdjectives[possessiveAdjective]+" "+Nouns[noun];
//lcd.print(Sentence);
}
uint8_t i=0;
void loop() {
uint8_t buttons = lcd.readButtons();
if (buttons) {
lcd.clear();
lcd.setCursor(0,0);
if (buttons & BUTTON_SELECT) {
sentence();
}
}
}

After doing much research, I have found a decent answer to my problem.
This code splits the verb, noun, and possessive adjective strings into tokens, and then selects a token from each string based on random integers. These tokens are then added up to form an imperative sentence. The final sentence is displayed on a 16X2 character LCD shield.
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
#define MAX_STRING_LEN 1000
char *Verbs = "abide accelerate accept accomplish achieve";
char *PossessiveAdjectives = "my your his her its our their";
char *Nouns = "account achiever acoustics act action activity";
char *p, *i;
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
int verbCount = 0,vc;
int adjectiveCount = 0,ac;
int nounCount = 0,nc;
for(vc=0;vc<strlen(Verbs);vc++){
if(Verbs[vc] == ' ')
verbCount++;
}
for(ac=0;ac<strlen(PossessiveAdjectives);ac++){
if(PossessiveAdjectives[ac] == ' ')
adjectiveCount++;
}
for(nc=0;nc<strlen(Nouns);nc++){
if(Nouns[nc] == ' ')
nounCount++;
}
int randVerb = random(1,verbCount+2);
int randPossessiveAdjective = random(1,adjectiveCount+2);
int randNoun = random(1,nounCount+2);
String Verb = subStr(Verbs, " ", randVerb);
String PossessiveAdjective = subStr(PossessiveAdjectives, " ", randPossessiveAdjective);
String Noun = subStr(Nouns, " ", randNoun);
String ImperativeSentence = Verb+" "+PossessiveAdjective+" "+Noun;
if(Verb.length()+PossessiveAdjective.length()+Noun.length()+2 > 16) {
if(Verb.length()+PossessiveAdjective.length()+1 > 16) {
if(Verb.length() > 16) {
setup;
}
else {
lcd.print(Verb);
lcd.setCursor(0, 1);
lcd.print(PossessiveAdjective);
lcd.print(" ");
lcd.print(Noun);
}
}
else {
lcd.print(Verb);
lcd.print(" ");
lcd.print(PossessiveAdjective);
lcd.setCursor(0, 1);
lcd.print(Noun);
}
}
else {
lcd.setCursor(0, 0);
lcd.print(ImperativeSentence);
}
Serial.println(ImperativeSentence);
}
uint8_t a=0;
void loop() {
uint8_t buttons = lcd.readButtons();
if (buttons) {
lcd.clear();
lcd.setCursor(0,0);
if (buttons & BUTTON_SELECT) {
setup();
}
}
}
char* subStr (char* str, char *delim, int index) {
char *act, *sub, *ptr;
static char copy[MAX_STRING_LEN];
int i;
strcpy(copy, str);
for (i = 1, act = copy; i <= index; i++, act = NULL) {
sub = strtok_r(act, delim, &ptr);
if (sub == NULL) break;
}
return sub;
}
This is clearly a work in progress. I still need to store a word bank on an SD card, and further simplify the code. Any suggestions would be greatly appreciated.

Related

Weird shapes as output( Strings)- C language

So i have a problem. I have to separate the first name, last name and hostname of email.
For example:
zephyr.extreme#gmail.com>> Input
Output=
First name= Zephyr
Last name= extreme
Host Name= gmail.com
I am not getting the desired result. I am getting some weird shapes as output.
Code:
#include <stdio.h>
int main()
{
char email[40], first[20],last[20],host[30];
printf("Enter the email= ");
gets(email);
int i;
while(email[i]!='\0')
{
while(email[i]!='.')
{
first[i]=email[i];
i++;
}
while(email[i]!='#')
{
last[i]=email[i];
i++;
}
while(email[i]!='\0')
{
host[i]=email[i];
i++;
}
}
puts(first);
puts(last);
puts(host);
}
Assuming the format will always be first.last#host..., use this code:
#include <stdio.h>
#include <string.h>
int main()
{
char email[40], first[20],last[20],host[30],name[40];
int firstDot,atSymbol;
int i;
int length;
char *token;
printf("Enter the email= ");
gets(email);
length = strlen(email);
for(i=0;i<length;i++){
if(email[i]=='.')
{
firstDot = i;
}
else if(email[i]=='#')
{
atSymbol = i;
}
}
strncpy(name,email,atSymbol);
name[atSymbol]= '\0';
token = strtok(name, ".");
/* walk through other tokens */
while( token != NULL )
{
printf( "%s\n", token );
token = strtok(NULL, ".");
}
strncpy(host,email+atSymbol,length-atSymbol);
host[length-atSymbol] = '\0';
puts(host);
}
So i updated the code, now the only problem is the last output.
After host name= gmail.com prints, but then some extra shapes are also printing. These are smile face and some weird symbols.
Code:
#include <stdio.h>
int main()
{
char email[40], first[20],last[20],host[30];
printf("Enter the email= ");
gets(email);
int i=0,j;
while(email[i]!='.')
{
first[i]=email[i];
i++;
}
i=0;
while(email[i]!='#')
{
last[i]=email[i];
i++;
}
j=i;
i=0;
while(email[j]!='\0')
{
host[i]=email[j];
j++;
i++;
}
printf("First Name= ");
puts(first);
printf("Last name= ");
puts(last);
printf("Host name= ");
puts(host);
}
C strings (char pointers) should be null-terminated. This means your string needs a '\0' character at its end so that string manipulation functions such as puts or strlen know where they end, in constrast to other languages where the string's length is stored together with it. The "weird shapes" you are seeing are just random data stored after the end of the string being interpreted as characters. When you call puts it just keeps outputting bytes-as-characters until it randomly finds a byte with value '\0'
You can solve this by adding a '\0' character to the end of the string after each of the blocks where you write a string.
while(email[i]!='.')
{
first[i]=email[i];
i++;
}
email[i] = '\0'; //same thing as email[i] = 0; but using a char makes what
//you're doing clearer

How to change the value of string from a cout

void extractWord (string& str)
I have to write a function that extracts the word between ‘*’.
For example, using the three test cases below:
string s = "This is to be *reversed*"
string s1 ="*Reversed* starts here";
string s2 = "This is *in* the middle";
and after each function call,
s=reversed, s1=Reversed, s2=in
So i figured out...
void extractWord (string& str)
{
char target= '*';
int idx1=0;
while (str[idx1] != target)
idx1=idx1+1;
int idx2=idx1+1;
while (str[idx2] != target)
idx2=idx2+1;
for(int i=0;i<sizeof(str);i++)
{
if ((i>idx1)&&(i<idx2))
cout<<str[i];
}
}
int main()
{
string s="This is to be *reversed*";
string s1 = "*Reversed* starts here";
string s2= "This is *in* the middle";
extractWord(s);
cout<<endl;
extractWord(s1);
cout<<endl;
extractWord(s2);
cout<<endl;
}
but how do I change the value of s into the output of this function?
I have modified your code a bit. I hope this solves your problem:
//#include "stdafx.h"
#include < string >
#include < iostream >
using namespace std;
void extractWord (string& str)
{
char target= '*';
int idx1=0;
while (str[idx1] != target)
idx1=idx1+1;
int idx2=idx1+1;
while (str[idx2] != target)
idx2=idx2+1;
str=str.substr(idx1+1,idx2-idx1-1); //changed
}
int main()
{
string s="This is to be *reversed*";
string s1 = "*Reversed* starts here";
string s2= "This is *in* the middle";
extractWord(s);
cout<<s<<endl; //changed
extractWord(s1);
cout<<s1<<endl; //changed
extractWord(s2);
cout<<s2<<endl; //changed
system("PAUSE");
return 0;
}
Now, every time you call void extractWord (string& str) it replaces string with only word between *'s.
I've used std::string::substr function. > http://www.cplusplus.com/reference/string/string/substr/
Other option is to make function that returns word between *'s.
//#include "stdafx.h"
#include < string >
#include < iostream >
using namespace std;
string extractWord (string& str)
{
char target= '*';
int idx1=0;
while (str[idx1] != target)
idx1=idx1+1;
int idx2=idx1+1;
while (str[idx2] != target)
idx2=idx2+1;
return str.substr(idx1+1,idx2-idx1-1);
}
int main()
{
string s="This is to be *reversed*";
string s1 = "*Reversed* starts here";
string s2= "This is *in* the middle";
string res;
res=extractWord(s);
cout<<res<<endl;
res=extractWord(s1);
cout<<res<<endl;
res=extractWord(s2);
cout<<res<<endl;
system("PAUSE");
return 0;
}
However, note that it doesn't work if your string do not contain two *'s or it has more than one word that has to be extracted. I hope this helped you.

C++ vector and string, count vowels

This program supposed to read a text and count the number of vowels and consonants. it should ignore any non alphabetic characters. the result should me something like this:
Enter your text: I have to TURN this..in before midnight!!
a, e, i, o, u, y
1, 3, 5, 2, 1, 0
There are 19 consonants.
but the result from my code is :
Enter your text: I have to TURN this..in before midnight!!
a, e, i, o, u, y
1, 3, 4, 2, 0, 0
There are 31 consonants.
I dont know what is happening!! Also this is an assignment and I have to use all these functions and I cannot add or remove them! I read couple of other ways to count and display the numbers but unfortunately the template was given...
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
using namespace std;
// FUNCTION PROTOTYPES GO HERE:
void init_vectors(vector<char> & vowels, vector<int> & frequencies);
string read_text(const string & prompt);
bool is_alphabetic(const char character);
void create_list(const string & str_text, vector<char> & vec_text);
bool is_member(const vector<char> & list, char character);
int find_index(const vector<char> & list, char character);
int compute_vowel_freqs(const vector<char> & text, const vector<char> & vowels, vector<int> & freqs);
void display_characters(const vector<char> & characters, const int colwidth);
void display_freqs(const vector<int> & freqs, const int colwidth);
int main()
{
vector<char> vowels;
vector<int> freqs;
string input;
vector<char> text;
int consonants(0);
const int COLUMNWIDTH = 2;
init_vectors(vowels, freqs);
input=read_text("Enter your text: ");
create_list(input, text);
compute_vowel_freqs(text, vowels, freqs);
display_characters(vowels, COLUMNWIDTH);
display_freqs(freqs, COLUMNWIDTH);
consonants = compute_vowel_freqs(text, vowels, freqs);
cout<<"There are "<< consonants<< " consonants."<<endl;
return 0;
}
void init_vectors(vector<char> & vowels, vector<int> & frequencies)
{
for (int i(0); i<6; i++) //i is loop variable
{
frequencies.push_back(0);
}
vowels.push_back('a');
vowels.push_back('e');
vowels.push_back('i');
vowels.push_back('o');
vowels.push_back('u');
vowels.push_back('y');
}
string read_text(const string & prompt)
{
string phrase;
cout<<prompt;
getline(cin,phrase);
return phrase;
}
bool is_alphabetic(const char character)
{
bool alphabet;
if ((character > 'a' && character < 'z')||(character > 'A' && character < 'Z'))
{
alphabet = true;
}
return alphabet;
}
void create_list(const string & str_text, vector<char> & vec_text)
{
for( int i = 0 ; i < str_text.length() ; i++)
{
if(is_alphabetic(str_text[i]))
vec_text.push_back(str_text[i]);
}
}
bool is_member(const vector<char> & list, char character)
{
bool vowel;
for (int i(0); i<list.size(); i++)
{
if (character == list[i])
{
vowel=true;
}
}
return vowel;
}
int find_index(const vector<char> & list, char character)
{
int index = -1;
for(int i=0; i<list.size(); i++)
{
if(character == list[i])
{
index = i;
break;
}
}
return index;
}
int compute_vowel_freqs(const vector<char> & text, const vector<char> & vowels, vector<int> & freqs)
{
int num_cons(0);
for(int i = 0 ; i < text.size() ; i++)
{
int index;
if(is_member(vowels, text[i]))
{
index = find_index(vowels , tolower(text[i]));
freqs[index]++;
}
else
num_cons++;
}
return num_cons;
}
void display_characters(const vector<char> & characters, const int colwidth)
{
for(int i=0; i<characters.size(); i++)
{
cout<<setw(colwidth)<<characters[i];
if((i+1)<characters.size())
{
cout<<",";
}
}
cout<<endl;
return;
}
void display_freqs(const vector<int> & freqs, const int colwidth)
{
for(int i=0; i<freqs.size(); i++)
{
cout<<setw(colwidth)<<freqs[i];
if((i+1)<freqs.size())
cout<<",";
}
cout<<endl;
return;
}
If you write hundreds of lines of code before you test any of it, you're bound to fail. Start small and simple, add complexity a little at a time, test at every step, and never add to code that doesn't work.
You should have tested these functions one by one as you wrote them. Here's the first problem:
bool is_alphabetic(const char character)
{
bool alphabet;
if ((character > 'a' && character < 'z')||(character > 'A' && character < 'Z'))
{
alphabet = true;
}
return false;
}
This always returns false, so nothing is recognized as text.
EDIT:
Second problem: is_member has exactly the same bug, with the same solution.
EDIT:
Third problem: I failed to notice that in this line in is_alphabetic:
if ((character > 'a' && character < 'z')||(character > 'A' && character < 'Z'))
You're using '>' and '<' when you should use ">=" and "<=". According to this function, 'a' and 'z' are not letters.
Look, you're still trying to test and fix this program as a whole. You must test it piecemeal. Pick a place in main and print out every variable that should have been assigned a value by then. This function may be useful:
void printVector(const vector<char> &V)
{
for(vector<char>::const_iterator citr=V.begin(); citr!=V.end(); ++citr)
cout << *citr;
cout << endl;
}
Then inspect the results. If a variable doesn't contain what it should, then something above that point is misbehaving. Trace the problem back to a function where good things go in but something bad comes out. Fix that, then look at the output again. I can't emphasize this enough: don't try to fix everything at once.
Your is_member is always false, so no char is vowel. And it results in 0 count.
bool is_member(const vector<char> & list, char character)
{
bool vowel = false; # this fix is not obligatory, I just made code look clearer
for (int i(0); i<list.size(); i++)
{
if (character == list[i])
{
vowel=true;
}
}
return vowel; # this should be fixed
}
By the way, the same issue is in the function 'is_alphabetic'. It is always false.

How to read a string value with a delimiter on Arduino?

I have to manage servos from a computer.
So I have to send manage messages from computer to Arduino. I need manage the number of servo and the corner. I'm thinking of sendin something like this : "1;130" (first servo and corner 130, delimeter ";").
Are there any better methods to accomplish this?
Here is my this code :
String foo = "";
void setup(){
Serial.begin(9600);
}
void loop(){
readSignalFromComp();
}
void readSignalFromComp() {
if (Serial.available() > 0)
foo = '';
while (Serial.available() > 0){
foo += Serial.read();
}
if (!foo.equals(""))
Serial.print(foo);
}
This doesn't work. What's the problem?
You can use Serial.readString() and Serial.readStringUntil() to parse
strings from Serial on arduino
You can also use Serial.parseInt() to read integer values from serial
Code Example
int x;
String str;
void loop()
{
if(Serial.available() > 0)
{
str = Serial.readStringUntil('\n');
x = Serial.parseInt();
}
}
The value to send over serial would be "my string\n5" and the result would be str = "my string" and x = 5
Note: Serial.available() inherits from the Stream utility class.
https://www.arduino.cc/reference/en/language/functions/communication/serial/available/
This is a Great sub I found. This was super helpful and I hope it will be to you as well.
This is the method that calls the sub.
String xval = getValue(myString, ':', 0);
This is The sub!
String getValue(String data, char separator, int index)
{
int found = 0;
int strIndex[] = {
0, -1 };
int maxIndex = data.length()-1;
for(int i=0; i<=maxIndex && found<=index; i++){
if(data.charAt(i)==separator || i==maxIndex){
found++;
strIndex[0] = strIndex[1]+1;
strIndex[1] = (i == maxIndex) ? i+1 : i;
}
}
return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}
Most of the other answers are either very verbose or very general, so I thought I'd give an example of how it can be done with your specific example using the Arduino libraries:
You can use the method Serial.readStringUntil to read until your delimiter from the Serial port.
And then use toInt to convert the string to an integer.
So for a full example:
void loop()
{
if (Serial.available() > 0)
{
// First read the string until the ';' in your example
// "1;130" this would read the "1" as a String
String servo_str = Serial.readStringUntil(';');
// But since we want it as an integer we parse it.
int servo = servo_str.toInt();
// We now have "130\n" left in the Serial buffer, so we read that.
// The end of line character '\n' or '\r\n' is sent over the serial
// terminal to signify the end of line, so we can read the
// remaining buffer until we find that.
String corner_str = Serial.readStringUntil('\n');
// And again parse that as an int.
int corner = corner_str.toInt();
// Do something awesome!
}
}
Of course we can simplify this a bit:
void loop()
{
if (Serial.available() > 0)
{
int servo = Serial.readStringUntil(';').toInt();
int corner = Serial.readStringUntil('\n').toInt();
// Do something awesome!
}
}
You need to build a read buffer, and calculate where your 2 fields (servo #, and corner) start and end. Then you can read them in, and convert the characters into Integers to use in the rest of your code. Something like this should work (not tested on Arduino, but standard C):
void loop()
{
int pos = 0; // position in read buffer
int servoNumber = 0; // your first field of message
int corner = 0; // second field of message
int cornerStartPos = 0; // starting offset of corner in string
char buffer[32];
// send data only when you receive data:
while (Serial.available() > 0)
{
// read the incoming byte:
char inByte = Serial.read();
// add to our read buffer
buffer[pos++] = inByte;
// check for delimiter
if (itoa(inByte) == ';')
{
cornerStartPos = pos;
buffer[pos-1] = 0;
servoNumber = atoi(buffer);
printf("Servo num: %d", servoNumber);
}
}
else
{
buffer[pos++] = 0; // delimit
corner = atoi((char*)(buffer+cornerStartPos));
printf("Corner: %d", corner);
}
}
It looks like you just need to correct
foo = ''; >>to>> foo = "";
foo += Serial.read(); >>to>> foo += char(Serial.read());
I made also shomething similar..:
void loop(){
while (myExp == "") {
myExp = myReadSerialStr();
delay(100);
}
}
String myReadSerialStr() {
String str = "";
while (Serial.available () > 0) {
str += char(Serial.read ());
}
return str;
}
This code reads string until it sees '>' character
void loop() {
// put your main code here, to run repeatedly:
String msg = getMessage();
}
String getMessage() {
String msg = "";
while (Serial.available()>0) {
msg = Serial.readStringUntil('>');
}
return msg;
}
It's universal parser
struct servo
{
int iServoID;
int iAngle;
};
std::vector<std::string> split(const std::string& str, const std::string& delim)
{
std::vector<std::string> tokens;
size_t prev = 0, pos = 0;
do
{
pos = str.find(delim, prev);
if (pos == std::string::npos) pos = str.length();
std::string token = str.substr(prev, pos-prev);
if (!token.empty()) tokens.push_back(token);
prev = pos + delim.length();
}
while (pos < str.length() && prev < str.length());
return tokens;
}
std::vector<servo> getServoValues(const std::string& message)
{
std::vector<servo> servoList;
servo servoValue;
std::vector<std::string> servoString;
std::vector<std::string> values = split(message, ",");
for (const auto& v : values)
{
servoString.clear();
servoString = split(v, ";");
servoValue.iServoID = atoi(servoString[0].c_str()); //servoString[0].toInt();
servoValue.iAngle = atoi(servoString[1].c_str());// servoString[1].toInt();
servoList.emplace_back(servoValue);
}
return servoList;
}
to call:
std::string str = "1;233,2;123";
std::vector<servo> servos = getServoValues(str);
for (const auto & a : servos)
std::cout<<a.iServoID << " " << a.iAngle << std::endl;
Result
1 233
2 123

Finding a substring of multiple occurences in a string [C++]

is there any STL algorithm or a standard way of finding how many occurences of particular substring are there in a string? For example in string:
'How do you do at ou'
the string "ou" appears twice. I tried some STL algorithms with and without predicates but what I found is that those algorithms from STL want to compare components of string which in my case is char but cannot? compare substrings.
I come up with something like this:
str - string
obj - substring we're looking for
std::string::size_type count_subs(const std::string& str, const std::string& obj)
{
std::string::const_iterator beg = str.begin();
std::string::const_iterator end = str.end();
std::string::size_type count = 0;
while ((beg + (obj.size() - 1)) != end)
{
std::string tmp(beg, beg + obj.size());
if (tmp == obj)
{
++count;
}
++beg;
}
return count;
}
thank you.
#include <string>
#include <iostream>
int Count( const std::string & str,
const std::string & obj ) {
int n = 0;
std::string ::size_type pos = 0;
while( (pos = obj.find( str, pos ))
!= std::string::npos ) {
n++;
pos += str.size();
}
return n;
}
int main() {
std::string s = "How do you do at ou";
int n = Count( "ou", s );
std::cout << n << std::endl;
}

Resources