I am working on a project for school and I am stuck on what I believe is just a small part but I cant figure it out.
Here is what I have so far:
#include <iostream>
#include <fstream>
#include <string>
#include <locale>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
int main(int argc, char* argv[])
{
set<string> setwords;
ifstream infile;
infile.open("words.txt"); //reads file "words.txt"
string word = argv[1]; // input from command line
transform(word.begin(), word.end(), word.begin(), tolower); // transforms word to lower case.
sort(word.begin(), word.end()); // sorts the word
vector<string> str; // vector to hold all variations of the word
do {
str.push_back(word);
}
while (next_permutation(word.begin(), word.end())); // pushes all permutations of "word" to vector str
if (!infile.eof())
{
string items;
infile >> items;
setwords.insert(items); //stores set of words from file
}
system("PAUSE");
return 0;
}
Now I need to compare the words from the file and the permutations stored in vector str
and print out the ones that are real words.
I know I need to use the find method of the set class. I am just not sure how to go about that. I was trying something like this with no luck, but my thought process is probably wrong.
for (unsigned int i = 0; i < str.size(); i++)
if (setwords.find(word) == str[i])
cout << str[i] << endl;
If you guys could help or point me in the right direction I would greatly appreciate it.
First, I'd like to say that this is a well-asked question. I appreciate new users that take the time to articulate their problem in detail.
The problem is that the find() method of a std::set<> returns an iterator object pointing to the value that it finds, or the end() of the container if it can't. When you compare it with str[i] (a string) it can't find a suitable overload of operator==() that takes both the iterator and a string.
Instead of making a full-on comparison with the string, you can instead compare the return value with end() to determine if it found the string:
if (setwords.find(str[i]) != setwords.end())
// ^^^^^^ ^^^^^^^^^^^^^^
If the expression returns true, then it sucessfully found the string inside the set.
There's also another potential problem I'd like to address in your code. Using if (!file.eof()) is the wrong way to condition your input. You should instead make the extract part of the condition, like this:
for (std::string item; infile >> item; )
{
setwords.insert(item);
}
Here's another way, using std::istream_iterator<>:
setwords.insert(std::istream_iterator<std::string>(infile),
std::istream_iterator<std::string>());
You actually are really close to having it right.
The set::find method doesn't return the value if it is found in the set, but rather an iterator object that points to the value. So your if statement is comparing the current string to the returned iterator object instead of the value that the iterator points to.
To get the value than an iterator points to, you just have to dereference it like you would a pointer, by prefixing it with an asterisk. Which means that you probably intended your if statement look like this:
if (*(setwords.find(word)) == str[i])
This would work for cases where the value was found in the set, but would be problematic for cases where the value was not found. If the value is not found, an iterator that points to the position after the last item in the set is returned - and you shouldn't try to dereference such an iterator (because it doesn't point to a valid object).
The way these checks are usually conducted is by comparing the returned iterator with the iterator that points to the end of the set (e.g., set::end, in this case). If the iterators do not match, that means the item was found.
if (setwords.find(word) != setwords.end())
cout << word << endl;
I think you need to write something like this:
for (unsigned int i = 0; i < str.size(); i++)
if (setwords.find(str[i]) != setwords.end())
cout << str[i] << endl;
But I think you don't need to store all permutations. You can store set of words with sorted letters. And compare it with sorted word.....
here is simpler solution
#include <iostream>
#include <fstream>
#include <string>
#include <locale>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
int main(int argc, char* argv[])
{
map<string, string> mapwords;
ifstream infile;
infile.open("words.txt"); //reads file "words.txt"
string word = argv[1]; // input from command line
transform(word.begin(), word.end(), word.begin(), tolower); // transforms word to lower case.
sort(word.begin(), word.end()); // sorts the word
if (!infile.eof())
{
string item;
infile >> item;
string sorted_item = item;
sort(sorted_item.begin(), sorted_item.end()); // sorts the word
mapwords.insert(make_pair(sorted_item, item)); //stores set of words from file
}
map<string, string>::iterator i = mapwords.find(word);
if(i != mapwords.end())
cout << i->second << endl;
system("PAUSE");
return 0;
}
Related
I would like to store a dictionary in a vector of lists. Each lists contains all words that have the same starting letter in the alphabet. (e. g. ananas, apple)
My problem is that I cannot read any words starting with "z" in my const char* array into the list.
Could someone explain to me why and how to fix this/ Is there a way to realize it with const char*? Thank you!
#include <iostream>
#include <list>
#include <vector>
#include <iterator>
#include <algorithm>
#include <string>
#include <fstream>
std::pair<bool, std::vector<std::list<std::string>> > loadwithList()
{
const char* prefix = "abcdefghijklmnopqrstuvwxyz";
std::vector<std::list<std::string>> dictionary2;
std::ifstream infile("/Users/User/Desktop/Speller/Dictionaries/large", std::ios::in);
if (infile.is_open())
{
std::list<std::string> data;
std::string line;
while (std::getline(infile, line))
{
if (line.starts_with(*prefix) && *prefix != '\0')
{
data.push_front(line);
}
else
{
dictionary2.push_back(data);
data.clear();
prefix++;
}
}
infile.close();
return std::make_pair(true, dictionary2);
}
std::cout << "Cant find file\n";
return std::make_pair(false, dictionary2);
}
int main()
{
auto [loaded, dictionary2] = loadwithList();
if (!loaded) return 1;
}
Answer is already given and problems are explained.
Basically you would need a double nested loop. Outer loop would read word by word, inner loop would check a mtach for each of the characters in "prefix". This will be a lot of looping . . .
And somehow not efficient. It would be better to take a std::mapfor storing the data in the first place. And if you really need a std::vectorof std::lists, then we can copy the data. We will take care to store only lowercase alpha characters as the key of the std::map.
For test purposes I loaded a list with words from here. There are roundabout 450'000 words in this list.
I used this for my demo program.
Please see below one potential solution proposal:
#include <iostream>
#include <fstream>
#include <map>
#include <list>
#include <vector>
#include <utility>
#include <string>
#include <cctype>
std::pair<bool, std::vector<std::list<std::string>> > loadwithList() {
std::vector<std::list<std::string>> data{};
bool resultOK{};
// Open File and check, if it could be opened
if (std::ifstream ifs{ "r:\\words.txt" }; ifs) {
// Here we will store the dictionary
std::map<char, std::list<std::string>> dictionary{};
// Fill dictionary. Read complete file and sort according to firstc character
for (std::string line{}; std::getline(ifs, line); )
// Store only alpha letters and words
if (not line.empty() and std::isalpha(line.front()))
// Use lower case start character for map. All all words starting with that character
dictionary[std::tolower(line.front())].push_back(line);
// Reserve space for resulting vector
data.reserve(dictionary.size());
// Move result to vector
for (auto& [letter, words] : dictionary)
data.push_back(std::move(words));
// All good
resultOK = true;
}
else
std::cerr << "\n\n*** Error: Could not open source file\n\n";
// And give back the result
return { resultOK , data };
}
int main() {
auto [result, data] = loadwithList();
if ( result)
for (const std::list<std::string>&wordList : data)
std::cout << (char)std::tolower(wordList.front().front()) << " has " << wordList.size() << "\twords\n";
}
You loose the first word of each letter after 'a'. This is because when you reach a word of the next letter, the if(line.starts_with(*prefix) && *prefix != '\0') fails and only then you go to the next letter but also go to the next word.
You loose the whole letter 'z' because after the last line in your file - the if(line.starts_with(*prefix) && *prefix != '\0') has succeeded at this point - the while (std::getline(infile, line)) terminates and you miss the dictionary2.push_back(data);.
I was inquiring about reading a sequence of words and storing the values in a vector. Then proceed to change each word in the vector to uppercase and print the out put with respect to eight word to a line. I think my code is either slow or running infinitely as i can't seem to achieve an output.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main() {
string word;
vector<string> text;
while (getline(cin, word)) {
text.push_back(word);
}
for (auto index = text.begin(); index != text.end(); ++index) {
for ( auto it = word.begin(); it != word.end(); ++it)
*it = toupper(*it);
/*cout<< index << " " << endl;*/
}
for (decltype(text.size()) i = 0; i != 8; i++)
cout << text[i] << endl;
return 0;
}
At least as far as I can tell, the idea here is to ignore the existing line structure, and write out 8 words per line, regardless of line breaks in the input data. Assuming that's correct, I'd start by just reading words from the input, paying no attention to the existing line breaks.
From there, it's a matter of capitalizing the words, writing them out, and (if you're at a multiple of 8, a new-line.
I would also use standard algorithms for most of the work, instead of writing my own loops to do the pars such as reading and writing the data. Since the pattern is basically just reading a word, modifying it, then writing out the result, it fits nicely with the std::transform algorithm.
Code to do that could look something like this:
#include <string>
#include <iostream>
#include <algorithm>
std::string to_upper(std::string in) {
for (auto &ch : in)
ch = toupper((unsigned char) ch);
return in;
}
int main() {
int count = 0;
std::transform(
std::istream_iterator<std::string>(std::cin),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout),
[&](std::string const &in) {
char sep = (++count % 8 == 0) ? '\n' : ' ';
return to_upper(in) + sep;
});
}
We could implement capitalizing each string as a second lambda, nested inside the first, but IMO, that starts to become a bit unreadable. Likewise, we could use std::tranform to implement the upper-case transformation inside of to_upper.
I'll rewrite my answer here:
Your outer for loop defines index to cycle through text, but you never use index inside it. The inner loop uses word, but word is still the last one the user entered. You should change the inner loop so that it uses index instead of word, like this:
for ( auto it = index->begin(); it != index->end(); ++it)
This is effectively an infinite loop:
while (getline(cin, word)) {
text.push_back(word);
}
getline(cin, word) reads a line (ending in '\n') from stdin, and puts it into word. It then returns cin itself (which will evaluate to true if the read was successful). You seem to be using it to get a space-delimited word, rather than a whole line, but that's not what it does. Since you put it in the condition of the while, after you enter a line, it will wait for another line.
This loop only breaks when getline fails. For example, by hitting an End of File character. I expect you're using the console and pressing Enter. In that case, you are never causing getline to fail. (If you're feeding a file into stdin, it should work.)
The typical solution to this is to have some sort of way of indicating a stop (such as an "Enter an empty line to stop" or "Write \"STOP\" to stop", and then checking for that before inserting the line into the vector). For you, the solution is to read in a SINGLE line, and then break it up into words (for example, using the sstream library).
You can detect whether the program is doing actual work (rather than waiting for more input) by viewing your CPU use. In Windows, this is CTRL+SHIFT+ESC -> Performance, and -> Processes to see your program in particular. You will find that the program isn't actually using the CPU (because it's waiting for more input).
You should try inserting print statements into your program to figure out where it gets up to. You will find it never goes past the for-loop.
Short Answer
for (string &str : vec)
{
transform(str.begin(), str.end(), str.begin(), [](char c) { return std::toupper(c); });
}
Complete working code as example:
#include <iostream>
#include <string>
#include <vector>
#include <cctype>
#include <algorithm>
using namespace std;
int main()
{
vector<string> vec;
string str;
while (cin >> str)
{
vec.push_back(str);
}
for (string &str : vec)
{
transform(str.begin(), str.end(), str.begin(), [](char c)
{ return toupper(c); });
}
for (auto str : vec)
{
cout << str << endl;
}
return 0;
}
I'm a C++ rookie, and having trouble with grasping some concepts, specifically with std::find in a while loop, checking an array.
I've got a bit of a PHP background, but am very new to the C++/lower level language. My specific issues are towards the end of the program where I have the while (check >> word){} loop. Visual basic is giving me an error on my std::find as well.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main(int argc, char* argv[]){
//Open the dictionary file(it is called words.txt).
ifstream myFile;
myFile.open("dictionary.txt");
//Define a vector of strings called words.
std::vector<string> dictionary;
//For each word in the dictionary file
string word;
while (myFile >> word){
//Append the word to the words vector.
dictionary.push_back(word);
}
//Open the file to be checked(the file is specified on the command line)
ifstream check;
check.open(argv[2]);
//For each word in that file
while (check >> word){
//check vector dictionary to see if check >> word exists
if (std::find(dictionary.begin(), dictionary.end(), word)){
//If the word is not contained in the dictionary vector print the word.
}
}
return 0;
}
I apologize if this has been answered, but I've looked at a few threads, and I'll keep looking through more until I get it, but I'm still a bit lost. I'll keep trying to solve my own problem as well!
Thank you for any help!
Edit: Visual basic was giving an intellisense error; however that went away when I changed my while loop to this
while (check >> word){
//check vector dictionary to see if check >> word exists
if (std::find(dictionary.begin(), dictionary.end(), word) == dictionary.end()){
//If the word is not contained in the dictionary vector print the word.
cout << word << endl;
}
}
You condition should be:
std::find(dictionary.begin(), dictionary.end(), word) == dictionary.end()
Note that in your case, you may use a map instead and then use
dictionary.count(word) == 0
I think one of your biggest problems is your mindset that C++ is a lower level language. You're taking rather the long way around on some of these things, and in the process making your own life quite a bit more difficult.
I'd probably do the job more like this:
std::ifstream in("dictionary.txt");
// read all the words from the file:
std::vector<std::string> dictionary{std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>() };
// If the words might not already be sorted, sort them:
// std::sort(dictionary.begin(), dictionary.end());
// open the file to check:
std::ifstream check(argv[2]);
// print out the words that aren't in the dictionary:
std::copy_if(std::istream_iterator<std::string>(check),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"),
[&](std::string const &word) {
return !std::binary_search(dictionary.begin(), dictionary.end(), word);
});
This will typically have a substantial speed advantage, because it's using a binary search instead of a linear search through the dictionary.
How about this
int len = dictionary.size();
bool flag = true;
for(int i = 0; i < len; i++)
{
if(dictionary[i] == word) {
flag = false;
break;
}
}
if(flag)
cout<<word<<"\n";
else
;
I got a string and I want to remove all the punctuations from it. How do I do that? I did some research and found that people use the ispunct() function (I tried that), but I cant seem to get it to work in my code. Anyone got any ideas?
#include <string>
int main() {
string text = "this. is my string. it's here."
if (ispunct(text))
text.erase();
return 0;
}
Using algorithm remove_copy_if :-
string text,result;
std::remove_copy_if(text.begin(), text.end(),
std::back_inserter(result), //Store output
std::ptr_fun<int, int>(&std::ispunct)
);
POW already has a good answer if you need the result as a new string. This answer is how to handle it if you want an in-place update.
The first part of the recipe is std::remove_if, which can remove the punctuation efficiently, packing all the non-punctuation as it goes.
std::remove_if (text.begin (), text.end (), ispunct)
Unfortunately, std::remove_if doesn't shrink the string to the new size. It can't because it has no access to the container itself. Therefore, there's junk characters left in the string after the packed result.
To handle this, std::remove_if returns an iterator that indicates the part of the string that's still needed. This can be used with strings erase method, leading to the following idiom...
text.erase (std::remove_if (text.begin (), text.end (), ispunct), text.end ());
I call this an idiom because it's a common technique that works in many situations. Other types than string provide suitable erase methods, and std::remove (and probably some other algorithm library functions I've forgotten for the moment) take this approach of closing the gaps for items they remove, but leaving the container-resizing to the caller.
#include <string>
#include <iostream>
#include <cctype>
int main() {
std::string text = "this. is my string. it's here.";
for (int i = 0, len = text.size(); i < len; i++)
{
if (ispunct(text[i]))
{
text.erase(i--, 1);
len = text.size();
}
}
std::cout << text;
return 0;
}
Output
this is my string its here
When you delete a character, the size of the string changes. It has to be updated whenever deletion occurs. And, you deleted the current character, so the next character becomes the current character. If you don't decrement the loop counter, the character next to the punctuation character will not be checked.
ispunct takes a char value not a string.
you can do like
for (auto c : string)
if (ispunct(c)) text.erase(text.find_first_of(c));
This will work but it is a slow algorithm.
Pretty good answer by Steve314.
I would like to add a small change :
text.erase (std::remove_if (text.begin (), text.end (), ::ispunct), text.end ());
Adding the :: before the function ispunct takes care of overloading .
The problem here is that ispunct() takes one argument being a character, while you are trying to send a string. You should loop over the elements of the string and erase each character if it is a punctuation like here:
for(size_t i = 0; i<text.length(); ++i)
if(ispunct(text[i]))
text.erase(i--, 1);
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string str = "this. is my string. it's here.";
transform(str.begin(), str.end(), str.begin(), [](char ch)
{
if( ispunct(ch) )
return '\0';
return ch;
});
}
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;//string is defined here.
cout << "Please enter a string with punctuation's: " << endl;//Asking for users input
getline(cin, s);//reads in a single string one line at a time
/* ERROR Check: The loop didn't run at first because a semi-colon was placed at the end
of the statement. Remember not to add it for loops. */
for(auto &c : s) //loop checks every character
{
if (ispunct(c)) //to see if its a punctuation
{
c=' '; //if so it replaces it with a blank space.(delete)
}
}
cout << s << endl;
system("pause");
return 0;
}
Another way you could do this would be as follows:
#include <ctype.h> //needed for ispunct()
string onlyLetters(string str){
string retStr = "";
for(int i = 0; i < str.length(); i++){
if(!ispunct(str[i])){
retStr += str[i];
}
}
return retStr;
This ends up creating a new string instead of actually erasing the characters from the old string, but it is a little easier to wrap your head around than using some of the more complex built in functions.
I tried to apply #Steve314's answer but couldn't get it to work until I came across this note here on cppreference.com:
Notes
Like all other functions from <cctype>, the behavior of std::ispunct
is undefined if the argument's value is neither representable as
unsigned char nor equal to EOF. To use these functions safely with
plain chars (or signed chars), the argument should first be converted
to unsigned char.
By studying the example it provides, I am able to make it work like this:
#include <string>
#include <iostream>
#include <cctype>
#include <algorithm>
int main()
{
std::string text = "this. is my string. it's here.";
std::string result;
text.erase(std::remove_if(text.begin(),
text.end(),
[](unsigned char c) { return std::ispunct(c); }),
text.end());
std::cout << text << std::endl;
}
Try to use this one, it will remove all the punctuation on the string in the text file oky.
str.erase(remove_if(str.begin(), str.end(), ::ispunct), str.end());
please reply if helpful
i got it.
size_t found = text.find('.');
text.erase(found, 1);
As per request of the fantastic fellas over at the C++ chat lounge, what is a good way to break down a file (which in my case contains a string with roughly 100 lines, and about 10 words in each line) and insert all these words into a std::set?
The easiest way to construct any container from a source that holds a series of that element, is to use the constructor that takes a pair of iterators. Use istream_iterator to iterate over a stream.
#include <set>
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
using namespace std;
int main()
{
//I create an iterator that retrieves `string` objects from `cin`
auto begin = istream_iterator<string>(cin);
//I create an iterator that represents the end of a stream
auto end = istream_iterator<string>();
//and iterate over the file, and copy those elements into my `set`
set<string> myset(begin, end);
//this line copies the elements in the set to `cout`
//I have this to verify that I did it all right
copy(myset.begin(), myset.end(), ostream_iterator<string>(cout, "\n"));
return 0;
}
http://ideone.com/iz1q0
Assuming you've read your file into a string, boost::split will do the trick:
#include <set>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
std::string astring = "abc 123 abc 123\ndef 456 def 456"; // your string
std::set<std::string> tokens; // this will receive the words
boost::split(tokens, astring, boost::is_any_of("\n ")); // split on space & newline
// Print the individual words
BOOST_FOREACH(std::string token, tokens){
std::cout << "\n" << token << std::endl;
}
Lists or Vectors can be used instead of a Set if necessary.
Also note this is almost a dupe of:
Split a string in C++?
#include <set>
#include <iostream>
#include <string>
int main()
{
std::string temp, mystring;
std::set<std::string> myset;
while(std::getline(std::cin, temp))
mystring += temp + ' ';
temp = "";
for (size_t i = 0; i < mystring.length(); i++)
{
if (mystring.at(i) == ' ' || mystring.at(i) == '\n' || mystring.at(i) == '\t')
{
myset.insert(temp);
temp = "";
}
else
{
temp.push_back(mystring.at(i));
}
}
if (temp != " " || temp != "\n" || temp != "\t")
myset.insert(temp);
for (std::set<std::string>::iterator i = myset.begin(); i != myset.end(); i++)
{
std::cout << *i << std::endl;
}
return 0;
}
Let's start at the top. First off, you need a few variables to work with. temp is just a placeholder for the string while you build it from each character in the string you want to parse. mystring is the string you are looking to split up and myset is where you will be sticking the split strings.
So then we read the file (input through < piping) and insert the contents into mystring.
Now we want to iterate down the length of the string, searching for spaces, newlines, or tabs to split the string up with. If we find one of those characters, then we need to insert the string into the set, and empty our placeholder string, otherwise, we add the character to the placeholder, which will build up the string. Once we finish, we need to add the last string to the set.
Finally, we iterate down the set, and print each string, which is simply for verification, but could be useful otherwise.
Edit: A significant improvement on my code provided by Loki Astari in a comment which I thought should be integrated into the answer:
#include <set>
#include <iostream>
#include <string>
int main()
{
std::set<std::string> myset;
std::string word;
while(std::cin >> word)
{
myset.insert(std::move(word));
}
for(std::set<std::string>::const_iterator it=myset.begin(); it!=myset.end(); ++it)
std::cout << *it << '\n';
}