Finding keyword in strings c++ - c++

I'm still fairly new to C++ and programming so I might just be missing something big here.
I'm trying to create a chatbot for a library, deals with opening times ect and other things. I want the chatbot to be able to pickup key words in a input and then be able to call the right function that will be able to give some text back to them.
For example:
user: what time is the library open till?
//chatbot picks up the key word 'open' and returns the right function
chatbot: the libraries open between 6 and 5
It shouldn't be as hard as I am finding it to be able to get the chatbot to do this.
The function I'm having trouble with:
std::string GetKeywords(){
std::string KQuery = GetQuery();
std::vector<std::string> keywords{"open", "opening", "times", "close", "closing", "shut"};
if(std::find(keywords.begin(), keywords.end(), KQuery) != keywords.end()){
std::cout << "Library is open when I say it is" << std::endl;
}
return 0;
};
This is returning a memory error and is the only place in my code which throws an issue.
All my code:
#include <iostream>
#include <string>
#include <vector>
#include "FinalProject.hpp"
//introducing funtions
void PrintIntro();
std::string GetQuery();
std::string RunScripts();
std::string GetKeywords();;
// introducing chatbot
RunScript ChatBot;
int main(){
PrintIntro();
GetQuery();
GetKeywords();
};
void PrintIntro(){
//printing introductory text to ask the user for input
std::cout << "Hi, I'm Librarius, I'm here to help you with University library queries" << std::endl;
std::cout << "I can help you with the following: \n Spaces to study \n Opening times \n Taking out books \n Returning books\n" << std:: endl;
std::cout << "Ask away!" << std::endl;
return;
};
std::string GetQuery(){
//getting input from the user
std::string Query = "";
std::getline(std::cin, Query);
if(Query.empty()){
//checking to see if the user hasnt entered anything
std::cout << "Hey! Why didnt you enter anything?! I don't want to waste my time!" << std::endl;
};
return Query;
};
std::string GetKeywords(){
std::string KQuery = GetQuery();
std::vector<std::string> keywords{"open", "opening", "times", "close", "closing", "shut"};
if(std::find(keywords.begin(), keywords.end(), KQuery) != keywords.end()){
std::cout << "Library is open when I say it is" << std::endl;
}
return 0;
};
//using the input got from the user to decide which script to run
//TODO analyse the users keywords and decide on a script to run
//TODO return an appropriate script
Thanks for your help!

The problem with
std::find(keywords.begin(), keywords.end(), KQuery)
is it is going to see if the entire string in KQuery matches one of your keywords. Since KQuery has a sentence in it, it isn't going to find a match. What you need to do is loop through all the keywords and see if KQuery.find(keyword) returns a valid result.
You can do that using std::find_if and a lambda like
std::find_if(keywords.begin(), keywords.end(),
[&](auto const& keyword){ return KQuery.find(keyword) != std::string::npos; });
This will return an iterator to the first keyword it finds in KQuery or keywords.end() if none of the keywords are found.

Related

C++: Passing vector to function and then calling function in main. Missing something

#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <vector>
using namespace std;
void getAnswer(std::vector<std::string> &answers, int nAnswers)
{
int index = rand() % nAnswers;
}
int main()
{
std::vector<string> answers;
answers.push_back("Most Certainly");
answers.push_back("Absolutely");
answers.push_back("Yes");
answers.push_back("You Can Bet On It");
answers.push_back("Odds look good");
answers.push_back("Let's talk about that some other time");
answers.push_back("Odds don't look so good");
answers.push_back("I think you know the answer to that question");
answers.push_back("I don't think I'm qualified to answer that question");
answers.push_back("Absolutely Not");
answers.push_back("I Don't Think So");
answers.push_back("Um...no");
std::vector<string> qAnswers(answers);
answers.size();
string questionAsked;
bool pgExit = false;
srand((unsigned int)time(NULL));
cout << "\nWelcome to the Magic 8Ball.\n";
cout << "\nAsk a question and I will predict the answer!\n" << endl;
//loop and ask the user to enter a question or enter "x" to stop
while (!pgExit) {
cout << "What is your question? (Type question or Enter 'x' to exit) " << endl;
//use getline to get the question
getline(cin, questionAsked);
//call getAnswer with your array and number of possible answers to get an answer
getAnswer(answers, answers.size());
//output the answer
if (questionAsked.compare("x") == 0)
{
cout << "Maybe next time. Have a good day.";
pgExit = true;
}
if (questionAsked.compare("") != 0 && questionAsked.compare("x") != 0)
{
getAnswer;
std::cout << getAnswer(answers, answers.size()) << std::endl;
}
}
}
The issue I am having is when I compile, it is saying 'no operator matches "<<" these operands. Standard operands are: std::ostream << void'
I am not sure I understand. I am passing the vector string and the vector size to void getAnswers to get the randomize for the answers. What am I missing?
Any help is greatly appreciated.
void getAnswer(std::vector<std::string> &answers, int nAnswers)
The void return "type" states explicitly that this function returns nothing so you cannot then go and attempt to use that return value in an expression:
std::cout << getAnswer(answers, answers.size()) << std::endl;
Assuming that you will eventually return a random answer from your list of answers (based on code to date), the first thing you should do is dummy up a reurn value:
std::string getAnswer(std::vector<std::string> &answers, int nAnswers)
{
// Just return last one for now (make sure you
// have at least one in the collection).
return answers[nAnswers - 1];
}
Then you can later adapt it to provide a random one. I could have just provided the complete function but, since this is probably educational work, you'll learn a lot more by doing this yourself (this answer is just to get you over your specific problem).
I have included some sample code at the bottom for you to look over (and for future readers who may not be doing the classwork) but I urge you to try yourself first.
As an aside, you should also be aware that you have some rather superfluous lines in your code, specifically:
answers.size();
getAnswer(answers, answers.size());
getAnswer;
None of these do anything useful, they simply evaluate the expression and throw away the result.
I'm also not why you attempt to create a second vector from the original, especially since you don't use it anywhere:
std::vector<string> qAnswers(answers);
As mentioned earlier, my sample code follows. Please do not use it verbatim if you value your marks (or integrity) - any educator worth their salt will be using plagiarism detection tools:
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <vector>
// Anon namespace to hide this in translation unit.
namespace {
// No need to pass in size, vector has this.
const std::string &getAnswer(const std::vector<std::string>answers) {
return answers[std::rand() % answers.size()];
}
};
int main() {
srand(static_cast<unsigned int>(time(nullptr)));
// Can be const if initialised rather than each entry pushed.
const std::vector<std::string> answers = {
"Most Certainly", "Absolutely", "Yes", "You Can Bet On It",
"Odds look good", "Let's talk about that some other time",
"Odds don't look so good",
"I think you know the answer to that question",
"I don't think I'm qualified to answer that question",
"Absolutely Not", "I Don't Think So", "Um...no",
};
std::cout << "\nWelcome to the Magic 8Ball.\n";
std::cout << "\nAsk a question and I will predict the answer!\n";
// Infinite loop, use break to exit.
while (true) {
// Ask and get response.
std::cout << "\nWhat is your question (x to exit)? " << std::endl;
std::string questionAsked;
std::getline(std::cin, questionAsked);
// Exit loop if told so.
if (questionAsked == "x") {
std::cout << "Maybe next time. Have a good day.\n\n";
break;
}
// Answer any non-blank question.
if (! questionAsked.empty()) {
std::cout << getAnswer(answers) << '\n';
}
}
}

Why can I access C++ map elements with a for loop but not individually?

I'm having a bit of a problem with the code below.
#include <iostream>
#include <fstream>
#include <string>
#include <map>
std::map<std::string, int> m; //Dictionary map
int main() {
std::ifstream dictionaryFile("dictionary.txt");
std::string str;
int probability = -1;
//Read dictionary.txt and assign to map
while(std::getline(dictionaryFile, str)) {
if(str.find("#!comment:") == std::string::npos) { //Not a comment
m.insert(std::pair<std::string, int>(str, probability));
}
else {
probability++;
}
}
dictionaryFile.close();
//Iterate and print through map -- THIS WORKS
std::map<std::string, int>::iterator pos;
for(pos = m.begin(); pos != m.end(); ++pos) {
std::cout << "Key: " << pos->first << std::endl;
std::cout << "Value: " << pos->second << "\n" << std::endl;
}
//Is "very" in the map? -- THIS DOES NOT WORK
std::cout << m.find("very")->second << std::endl;
if(m.find("very") != m.end()) {
std::cout << "found it" << std::endl;
} else {
std::cout << "did not find it" << std::endl;
}
}
I read in the "dictionary.txt" file, and insert each word into a map. Either 1 or 2 is the value associated with that key, depending on the probability of the word.
I'm able to iterate through the map and print it's elements from within a for-loop, as shown. But I'm unable to access each element individually with m.find(), m.count(), or the [] operator. Each of those show as if the map is empty.
Do I have a piece of syntax wrong? Have I discovered a bug in std::map? Any help would be appreciated!
Here is dictionary.txt if you would like it.
Your file contains Windows CRLF line endings \r\n. These are automatically translated into \n with the default istream processing on Windows. However, you are on a Linux system that will be treating your \r character as nothing particularly special.
There are various ways around this. The simplest would be to not use such files as inputs on Linux. You can find answers elsewhere on this site for how to convert line-endings in the shell.
If you absolutely want your program to handle them, then you need to introduce some extra code. It can be as simple as checking the last character:
if (!str.empty() && str.back() == '\r')
str.pop_back();
For pre-C++11 standard library that doesn't have std::string::pop_back, you can just call str.erase(str.size()-1) instead.

How to convert C++ string array to json?

I have started implementing Microsoft Cognitive Services using C++. I have a C++ String array(faceIds array)
string faceIds[] ={
"29e874a8-a08f-491f-84e8-eac263d51fe1",
"6f89f38a-2411-4f6c-91b5-15eb72c17c22",
"7284b730-6dd7-47a3-aed3-5dadaef75d76",
"1fc794fa-3fd4-4a78-af11-8f36c4cbf14c",
"3e57afca-bd1d-402e-9f96-2cae8dbdfbfa",
"c2a4e0f5-4277-4f5a-ae28-501085b05209",
"23b5910e-9c32-46dd-95f3-bc0434dff641"
};
Then, I try to convert string array(C++) to json string.
JSONObject jsnobject = new JSONObject(10);
JSONArray jsonArray = jsnobject.getJSONArray(faceIds);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject explrObject = jsonArray.getJSONObject(i);
}
But, I got problem. So, My question is, How to convert C++ string array to json?
Thank in Advance.
Your question doesn't precisely identify your input and expected output. Are you parsing the C++ from a file? I can't tell.
If the first code block is an autogenerated input file and will always have that whitespace pattern and the JSON equivalent is your desired output, replace the first line with "[\n" and the last line with "]/n" and you're done.
If you can't guarantee the white space pattern of the input file, then you will need a C++ parser to generate an AST (abstract symbol tree) that you can traverse to find the faceIds array RHS (right hand side) and then do the same thing as shown below from that AST collection.
If you simply want to iterate in C++ through faceIds, then the following code should produce the desired JSON string:
#include <iostream>
#include <sstream>
std::string faceIds[] = {
"29e874a8-a08f-491f-84e8-eac263d51fe1",
"6f89f38a-2411-4f6c-91b5-15eb72c17c22",
"7284b730-6dd7-47a3-aed3-5dadaef75d76",
"1fc794fa-3fd4-4a78-af11-8f36c4cbf14c",
"3e57afca-bd1d-402e-9f96-2cae8dbdfbfa",
"c2a4e0f5-4277-4f5a-ae28-501085b05209",
"23b5910e-9c32-46dd-95f3-bc0434dff641"
};
int main() {
std::ostringstream ostr;
ostr << '[' << std::endl;
int last = std::extent<decltype(faceIds)>::value - 1;
int i = 0;
while (i < last)
ostr << " \"" << faceIds[i ++] << "\"," << std::endl;
ostr << " \"" << faceIds[i] << "\"" << std::endl;
ostr << ']' << std::endl;
std::cout << ostr.str();
return 0;
}
If you want some library's object representation, then you'll have to identify what library you are using so we can review its API. Whatever library you use, you could always just run whatever parse method it has on ostr.str() above, but we could find a more efficient method to build the equivalent JSON tree if you identified the JSON library. One can't uniquely identify the library from an object name like JSONObject, which is a class name used in dozens of libraries.
This is a robust cross platform solution to working with JSON in C++ https://github.com/nlohmann/json. I'm sure Microsoft has some library locked to their own OS too. The examples are clear.
I think the nlohmann c++ library is useful in your case.

doubts on writing test case for a function

I have a function changeUserPassword() in user.cpp and I wanted to do a cppUnit test on it.
user.cpp
int User::changeUserPassword()
{
std::vector<user>::iterator it;
std::ifstream readFile("info.txt");
while(readFile >> userName >> password)
{
userDetails.push_back(user(userName,password));
}
readFile.close();
std::cout << "Please enter a user name that the password will be reset \n";
std::cin >> name;
it = std::find(userDetails.begin(),userDetails.end(),user(name,name));
if (it !=userDetails.end())
{
std::cout << "Please enter a new password" << std::endl;
std::cin >> newPassword;
it->setPassword(newPassword);
std::ofstream out("tempFile.txt");
for (it =userDetails.begin(); it !=userDetails.end(); it++) {
std::cout << it->getUserName() << " " << it->getPassword() << "\n";
out << it->getUserName() << " " << it->getPassword() << std::endl;
}
out.close();
remove("info.txt");
rename("tempfile.txt","info.txt");
}
else
{
it++;
}
return 0;
}
testcase.h
#ifndef TESTCASE_H
#define TESTCASE_H
#include "user.h"
#include <cppunit/TestCase.h>
#include <cppunit/extensions/HelperMacros.h>
class csci222TestCase : public CPPUNIT_NS::TestFixture {
CPPUNIT_TEST_SUITE(testcase);
CPPUNIT_TEST (testChangePassword);
CPPUNIT_TEST_SUITE_END();
public:
protected:
void testChangePassword(void);
private:
user testChangeUserPassword;
};
#endif
testcase.cpp
void testcase::testChangePassword(void) {
std::cout << "\n";
CPPUNIT_ASSERT_EQUAL(testChangeUserPassword.changeUserPassword(),0);
}
The thing is, I feel that the way I wrote my test case for changeUserPassword() isn't testing for anything. It's more like running the method and once it finished, it will return 0. How should I or what should I do to improve on the test case?
This function is not a good candidate for unit testing. It has file and user input for starters. You probably really only want to test the line
it->setPassword(newPassword);
which is actually what 'sets' the password, presumably. For that you would call the function in the unit test with a given password, then in turn do a getPassword() and see if it was changed to what you expected.
If you really want to test that function as-is, you would need to look into stubs and/or mock objects. For that you would need to refactor your code with some simple dependency injection, so you could swap the disk file I/O with some memory file I/O, for example. But I don't recommend this path.
To address the more conceptual issue that you have, your testChangePassword() function should just check that the password was changed. As it stands, all you're really testing for, in effect, is that the function doesn't throw an exception.
To sum up, your unit test should ideally be of the form:
user testuser;
testuser.setPassword( "Fred");
std::string pwd = testuser.getPassword();
CPPUNIT_ASSERT_EQUAL( "Fred", pwd);
In your case you should also try:
1. changing a password several times.
2. changing password to the same password.
3. changing a non existing password.
4. Changing the password on one with illegal characters.
5. Reading from a non-existing file.
6. Reading an invalid format of data from the file.
7. Check that you clean all fields of the your object unnecessary data when you complete the function.

PugiXML C++ getting content of an element (or a tag)

Well I'm using PugiXML in C++ using Visual Studio 2010 to get the content of an element, but the thing is that it stops to getting the value when it sees a "<" so it doesn't get the value, it just gets the content till it reaches a "<" character even if the "<" is not closing its element. I want it to get till it reaches its closing tag even if it ignores the tags, but only the text inside of the inner tags, at least.
And I also would like to know how to get the Outer XML for example if I fetch the element
pugi::xpath_node_set tools = doc.select_nodes("/mesh/bounds/b");
what do I do to get the whole content which would be " Link Till here"
this content is the same given down here:
#include "pugixml.hpp"
#include <iostream>
#include <conio.h>
#include <stdio.h>
using namespace std;
int main//21
() {
string source = "<mesh name='sphere'><bounds><b id='hey'> <a DeriveCaptionFrom='lastparam' name='testx' href='http://www.google.com'>Link Till here<b>it will stop here and ignore the rest</b> text</a></b> 0 1 1</bounds></mesh>";
int from_string;
from_string = 1;
pugi::xml_document doc;
pugi::xml_parse_result result;
string filename = "xgconsole.xml";
result = doc.load_buffer(source.c_str(), source.size());
/* result = doc.load_file(filename.c_str());
if(!result){
cout << "File " << filename.c_str() << " couldn't be found" << endl;
_getch();
return 0;
} */
pugi::xpath_node_set tools = doc.select_nodes("/mesh/bounds/b/a[#href='http://www.google.com' and #DeriveCaptionFrom='lastparam']");
for (pugi::xpath_node_set::const_iterator it = tools.begin(); it != tools.end(); ++it) {
pugi::xpath_node node = *it;
std::cout << "Attribute Href: " << node.node().attribute("href").value() << endl;
std::cout << "Value: " << node.node().child_value() << endl;
std::cout << "Name: " << node.node().name() << endl;
}
_getch();
return 0;
}
here is the output:
Attribute Href: http://www.google.com
Value: Link Till here
Name: a
I hope I was clear enough,
Thanks in advance
My psychic powers tell me you want to know how to get the concatenated text of all children of the node (aka inner text).
The easiest way to do that is to use XPath like that:
pugi::xml_node node = doc.child("mesh").child("bounds").child("b");
string text = pugi::xpath_query(".").evaluate_string();
Obviously you can write your own recursive function that concatenates the PCDATA/CDATA values from the subtree; using a built-in recursive traversing facility, such as find_node, would also work (using C++11 lambda syntax):
string text;
text.find_node([&](pugi::xml_node n) -> bool { if (n.type() == pugi::node_pcdata) result += n.value(); return false; });
Now, if you want to get the entire contents of the tag (aka outer xml), you can output a node to string stream, i.e.:
ostringstream oss;
node.print(oss);
string xml = oss.str();
Getting inner xml will require iterating through node's children and appending their outer xml to the result, i.e.
ostringstream oss;
for (pugi::xml_node_iterator it = node.begin(); it != node.end(); ++it)
it->print(oss);
string xml = oss.str();
That's how XML works. You can't embed < or > right in your values. Escape them (e.g. using HTML entities like < and >) or define a CDATA section.
I've struggled a lot with the issue of parsing subtree including all elements and sub-nodes - the easiest way is almost what shown here:
You should use this code:
ostringstream oss;
oNode.print(oss, "", format_raw);
sResponse = oss.str();
Instead of oNode use the node that you want, if needed use pugi:: before every function.