So I have a couple methods defined, one checks if the next is a word, one checks if there is a next word.
I take inputs for the text files, add the files content to a vector, then use the for loop
I can make it work by simply doing a for loop through the vector:
for (int x = 0; x != v2.size(); ++x) {
But I want to use my two methods
bool ReadWords::isNextWord()
and
string ReadWords::getNextWord()
{
//take the input of the file until end of list is reached and return it
}
So how would I do something like
vector<string> bigTextFile;
vector<string> vct;
while(vct.isNextWord) {
vct.getNextWord
if(vct.getNextWord == bigTextFile[i] {
counter++
}
}
Let me know if you need any more of the code
What about this:
vector<string> bigTextFile;
vector<string> vct;
while(ReadWords::isNextWord(vct))
{
string nextWord = ReadWords::getNextWord(vct);
if(nextWord == bigTextFile[i])
{
counter++;
}
}
You could make your functions take in a vector as a parameter then do the work on the vector inside each function. I'm not 100% sure what your implementation is since its not the complete code.
I'm not sure why are you trying to write an object, but here is how I would have approached the problem:
Step 1. Create a predicate for the words
bool is_needed_word(const std::string& word, const std::vector<std::string>& needed_words)
{
if (std::find(needed_words.begin(),
needed_words.end(),
word) != needed_words.end())
return true;
}
//some where in your function
std::vector<std::string> needed_words{"apple", "moon", "etc"};
auto predicate = [&needed_words](const auto& word)
{return is_needed_word(word, needed_words);}
Step 2. Copy/count/whatever you want to do with the words in the stream
//copying
std::vector<std::string> extracted_words;
std::ifstream input_file{"input.txt"};
//check if opened correctly
std::copy_if(std::istream_iterator<std::string>(input_file), {},
std::back_inserter(extracted_words));
//counting
std::size_t occurrence_counter = std::count_if(std::istream_iterator<std::string>(input_file),{},predicate)
Related
I have been trying to remove the value False and 0;0 from a vector<string> plan; containing the following
1003;2021-03-09;False;0;0;1678721F
1005;2021-03-05;False;0;0;1592221D
1005;2021-03-06;False;0;0;1592221D
1003;2021-03-07;False;0;0;1592221D
1003;2021-03-08;False;0;0;1592221D
1004;2021-03-09;False;0;0;1592221D
1004;2021-03-10;False;0;0;1592221D
1001;2021-03-11;False;0;0;1592221D
but the solutions I have found only work with int, and I tried the following
remove(plan.begin(), plan.end(), "False");
also with erase, but it didn't work
what is the mistake that I am making, or how should I do to eliminate the values that I want, which are in the position [2] [3] and [4], thanks for any help.
[Note: With the assumption 1003;2021-03-09;False;0;0;1678721F corresponding to a row inside std::vector<string>]
std::remove : Removes from the vector either a single element (position) or a range of elements ([first, last)).
In case std::vector<string> plan contains value False then it is removed.
std::vector < std::string > plan =
{
"1003","2021-03-09","False","0;0","1678721F"
};
std::remove(plan.begin(),plan.end(),"False");
In your case you need to remove given sub-string from each row of the plan. You need to iterate through all the rows to remove given value using std::string::erase.
std::vector < std::string > plan =
{
"1003;2021-03-09;False;0;0;1678721F",
"1005;2021-03-05;False;0;0;1592221D",
"1005;2021-03-06;False;0;0;1592221D",
"1003;2021-03-07;False;0;0;1592221D",
"1003;2021-03-08;False;0;0;1592221D",
"1004;2021-03-09;False;0;0;1592221D",
"1004;2021-03-10;False;0;0;1592221D",
"1001;2021-03-11;False;0;0;1592221D"};
for (auto & e:plan)
{
//As position of False;0;0; is at a fixed index, i.e: from index:16, 10 characters are removed
e.erase (16, 10);
}
To generalize, You can make use of std::String::find to find a sub-string and erase it.
void removeSubstrs(string& s, string p) {
string::size_type n = p.length();
for (string::size_type i = s.find(p);
i != string::npos;
i = s.find(p))
s.erase(i, n);
}
int
main ()
{
std::vector < std::string > plan =
{
"1003;2021-03-09;False;0;0;1678721F",
"1005;2021-03-05;False;0;0;1592221D",
"1005;2021-03-06;False;0;0;1592221D",
"1003;2021-03-07;False;0;0;1592221D",
"1003;2021-03-08;False;0;0;1592221D",
"1004;2021-03-09;False;0;0;1592221D",
"1004;2021-03-10;False;0;0;1592221D",
"1001;2021-03-11;False;0;0;1592221D"};
for (auto & e:plan)
{
removeSubstrs (e, ";False;0;0");
}
for (auto e:plan)
std::cout << e << std::endl;
return 0;
}
[Note: This answer assumes that each line corresponds to an element in the vector]
With the statement
remove(plan.begin(), plan.end(), "False");
you try to remove all elements from the vector that are equal to "False".
You need to iterate over the vector and erase the sub-string from each and every string in the vector.
For example you can use a range for loop to iterate over all the strings (or rather references to them), and then use the std::string functions find to find the sub-strings you want to remove and replace to replace the sub-strings with empty strings (i.e. nothing).
If you are sure that there is only one occurrence of "First" and "0;0" in your vector, you can use something like this:
std::string EraseFirstSubString(
const std::string & main_str,
const std::string & sub_str)
{
std::string new_main_str = main_str;
size_t pos = new_main_str.find(sub_str);
if (pos != std::string::npos)
{
new_main_str.erase(pos, sub_str.length());
}
return new_main_str;
}
int main()
{
std::vector<std::string> plan = {
"1003;2021-03-09;False;0;0;1678721F",
"1005;2021-03-05;False;0;0;1592221D",
"1005;2021-03-06;False;0;0;1592221D",
"1003;2021-03-07;False;0;0;1592221D",
"1003;2021-03-08;False;0;0;1592221D",
"1004;2021-03-09;False;0;0;1592221D",
"1004;2021-03-10;False;0;0;1592221D",
"1001;2021-03-11;False;0;0;1592221D"
};
for (std::string & str : plan)
{
str = EraseFirstSubString(str, "False");
str = EraseFirstSubString(str, "0;0");
}
};
But, if you think that you may have many occurrences of those sub-strings, you should improve a little bit your sub-string removing mechanism like this:
std::string EaraseSubStrings(
const std::string & main_str,
const std::string & sub_str)
{
std::string new_main_str = main_str;
size_t pos = new_main_str.find(sub_str);
while (pos != std::string::npos)
{
new_main_str.erase(pos, sub_str.length());
pos = new_main_str.find(sub_str);
}
return new_main_str;
}
If you already have a vector of individual std::string objects, you can easily use the operations that the strings library offers.
#include <algorithm>
#include <vector>
#include <string>
// before C++20 change constexpr to inline
constexpr void change(std::vector<std::string>& sv, std::string const& rem) {
for_each(beign(sv),end(sv), [&rem](std::string& s) {
s.erase(std::min(s.size(),s.find(rem)), rem.size());
});
}
I am trying to make a program which removes all anagrams from vector. I sorted them alphabetical and i put them in a class, hopefully it would be easier to delete de anagrams, but it didnt actually helped.
INPUT:{"have","ahve","frame","avhe","farme","code","abc","ehav"}
OUTPUT:{"code","abc"}
note- all words are lowercase
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;
struct Fraza{
string word;
string word_alph;
Fraza(){};
Fraza(string words,string word_alph){
this->word=move(word);
this->word_alph=move(word_alph);
}
void printverify() const{
cout<<word<<" is sorted "<<word_alph<<endl;
}
void print() const{
cout<<word<<endl;
}
};
int main() {
Fraza fraza [100];
vector<Fraza>text;
string tmp;
string tmpa;
// i am taking the input, store the words temporary in tmpa and sort tmpa, then i put them in struct
while(cin>>tmp){
tmpa=tmp;
sort(tmpa.begin(),tmpa.end());
text.emplace_back(Fraza(tmp,tmpa));
}
// trying to delete the words
for(int i=0;i<text.size();i++){
for(int j=i+1;j<text.size();j++) {
if (fraza[i].word_alph==fraza[j].word_alph) {
text.erase(text.begin()+i);
text.erase(text.begin()+j);
}
}
}
for(const auto& it:text)
it.printverify();
for(const auto& it:text)
it.print();
return 0;
}
so far i know that it might work comparing the sorted word. If 2 words are the same, i should try delete the words on the specific position
There are several bugs:
The Fraza constructor has a parameter called "words" that you don't use; this->word = std::move(word) moves from the member to itself. (Get familiar with the initialization list).
You have both an array fraza and a vector text. All the words are in text, but you compare the (empty) strings in fraza.
After you have erased the i:th string, the previously j:th string is the j-1:th string.
Fixing these bugs makes your program almost work, except it leaves behind one copy of anagrams that occur an odd number of times (since you remove an even number of them, there will always be one left).
I would approach this with a table.
First, encapsulate the sorting and give Fraza an ordering that makes anagrams equivalent:
class Fraza
{
public:
Fraza(const std::string& w)
: word(w), sorted(w)
{
std::sort(sorted.begin(), sorted.end());
}
bool operator< (const Fraza& other) const
{
return sorted < other.sorted;
}
const std::string& str() const { return word; }
private:
std::string word;
std::string sorted;
};
Then use std::map as a counting table:
int main()
{
// Count the words.
std::map<Fraza, int> table;
std::string word;
while (std::cin >> word)
{
table[word] += 1;
}
// Print the unique ones.
for (const auto& it: table)
{
if (it.second == 1)
{
std::cout << it.first.str() << std::endl;
}
}
}
I have two strings, i.e: APPLE and APPLEA. I want to iterate over APPLE and check if its characters belong to APPLEA. I have done this:
int counter=0;
for (j=0;j<dictionary[i].size();j++)
{
if (word_to_match.find(dictionary[i][j]) != std::string::npos)
{
counter++;
}
}
Where dictionary is just a std::vector that has APPLE and other words.
Is it possible to avoid the for loop by using std::transform or another tool?
----------------------------------EDIT------------------------------------
I have this, I dont know if it could be even cleaner
std::for_each(dictionary[i].begin(),dictionary[i].end(),[&word_to_match,&counter](char const &c){
if (word_to_match.find(c) != std::string::npos)
{
counter++;
}
});
How about std::count_if
int count = std::count_if(dictionary.begin(), dictionary.end(), [&](const std::string& s){
return word_to_match.find(s) != std::string::npos;
});
assuming dictionary is a container for std::string, eg. std::vector<std::string>. You can modify it accordingly to match your case.
I have input coming in form a file input.txt as two columns of strings such as:
string1 string2
string3 string4
etc.
I am trying to number the strings in ascending order starting form 0 but in such a way that repeating strings don't get assigned new values but keep the once already assigned to them.
I decided to use a set::find operation to do this, but I am having a hard time making it work. Here's what I have so far:
int main(int argc, char* argv[]) {
std::ifstream myfile ("input.txt");
std::string line;
int num = 0; // num is the total number of input strings
if (myfile.is_open()) {
while(std::getline(myfile, line)) {
++num;
}
}
std::string str1, str1; // strings form input
int str1Num, str2Num; // numbers assigned to strings
int i = 0; // used to assign values to strings
StringInt si;
std::vector<StringInt> saveStringInts(num);
std::set<std::string> alreadyCounted(num, 0);
std::set<std::string>::iterator sit;
std::ifstream myfile2 ("input.txt");
if (myfile2.is_open()) {
while(myfile2.good()) {
// read in input, put it in vars below
myfile2 >> str1 >> str2;
// if strings are not already assigned numbers, assign them
if ((*(sit = alreadyCounted.find(str1)).compare(str1) != 0) { // doesn't work
str1Num = i++;
alreadyCounted.insert(str1);
saveStringInts.push_back(StringInt(str1Num));
}
else {
str1Num = si->getNum(str1);
}
if ((*(sit = alreadyCounted.find(str2)).compare(str2) != 0) {
str2Num = i++;
alreadyCounted.insert(str2);
saveStringInts.push_back(StringInt(str2Num));
}
else {
str2Num = si->getNum(str2);
}
// use str1 and str2 in the functions below before the next iteration
}
}
Unfortunately, I tried other approaches and now completely stuck. If you know how to fix my code or can suggest a better way to accomplish my task, I would greatly appreciate your help.
You need to compare std::set<int>::iterator against the end() iterator of your set, rather than dereferencing the iterator and comparing its value against something! Actually, derferencing the end() iterator is undefined behavior:
if ((*(sit = alreadyCounted.find(str1)).compare(str1) != 0) // WRONG: don't do that!
should really be
if (alreadyCounted.find(str1) != alreadyCounted.end())
... and likewise for the other string. Personally, I would use a different technique, though: when insert()ing into a std::set<T>, you get back a pair of an iterator and an indicator whether the object was inserted. The latter together with the current set's size give the next value, e.g.:
bool result = alreadyCounted.insert(str1).second;
strNum1 = result? alreadyCounted.size() - 1: si->getNum(str1);
I am currently trying to count the number of words in a file. After this, I plan to make it count the words between two words in the file. For example. My file may contain. "Hello my name is James". I want to count the words, so 5. And then I would like to count the number of words between "Hello" and "James", so the answer would be 3. I am having trouble with accomplishing both tasks.
Mainly due to not being exactly sure how to structure my code.
Any help on here would be greatly appreciated. The code I am currently using is using spaces to count the words.
Here is my code:
readwords.cpp
string ReadWords::getNextWord()
{
bool pWord = false;
char c;
while((c = wordfile.get()) !=EOF)
{
if (!(isspace(c)))
{
nextword.append(1, c);
}
return nextword;
}
}
bool ReadWords::isNextWord()
{
if(!wordfile.eof())
{
return true;
}
else
{
return false;
}
}
main.cpp
main()
{
int count = 0;
ReadWords rw("hamlet.txt");
while(rw.isNextWord()){
rw.getNextWord();
count++;
}
cout << count;
rw.close();
}
What it does at the moment is counts the number of characters. I'm sure its just a simple fix and something silly that I'm missing. But I've been trying for long enough to go searching for some help.
Any help is greatly appreciated. :)
Rather than parse the file character-by-character, you can simply use istream::operator<<() to read whitespace-separated words. << returns the stream, which evaluates to true as a bool when the stream can still be read from.
vector<string> words;
string word;
while (wordfile >> word)
words.push_back(word);
There is a common formulation of this using the <iterator> and <algorithm> utilities, which is more verbose, but can be composed with other iterator algorithms:
istream_iterator<string> input(wordfile), end;
copy(input, end, back_inserter(words));
Then you have the number of words and can do with them whatever you like:
words.size()
If you want to find "Hello" and "James", use find() from the <algorithm> header to get iterators to their positions:
// Find "Hello" anywhere in 'words'.
const auto hello = find(words.begin(), words.end(), "Hello");
// Find "James" anywhere after 'hello' in 'words'.
const auto james = find(hello, words.end(), "James");
If they’re not in the vector, find() will return words.end(); ignoring error checking for the purpose of illustration, you can count the number of words between them by taking their difference, adjusting for the inclusion of "Hello" in the range:
const auto count = james - (hello + 1);
You can use operator-() here because std::vector::iterator is a “random-access iterator”. More generally, you could use std::distance() from <iterator>:
const auto count = distance(hello, james) - 1;
Which has the advantage of being more descriptive of what you’re actually doing. Also, for future reference, this kind of code:
bool f() {
if (x) {
return true;
} else {
return false;
}
}
Can be simplified to just:
bool f() {
return x;
}
Since x is already being converted to bool for the if.
To count:
std::ifstream infile("hamlet.txt");
std::size_t count = 0;
for (std::string word; infile >> word; ++count) { }
To count only between start and stop:
std::ifstream infile("hamlet.txt");
std::size_t count = 0;
bool active = false;
for (std::string word; infile >> word; )
{
if (!active && word == "Hello") { active = true; }
if (!active) continue;
if (word == "James") break;
++count;
}
I think "return nextword;" should instead be "else return nextword;" or else you are returning from the function getNextWord every time, no matter what the char is.
string ReadWords::getNextWord()
{
bool pWord = false;
char c;
while((c = wordfile.get()) !=EOF)
{
if (!(isspace(c)))
{
nextword.append(1, c);
}
else return nextword;//only returns on a space
}
}
To count all words:
std::ifstream f("hamlet.txt");
std::cout << std::distance (std::istream_iterator<std::string>(f),
std::istream_iterator<std::string>()) << '\n';
To count between two words:
std::ifstream f("hamlet.txt");
std::istream_iterator<std::string> it(f), end;
int count = 0;
while (std::find(it, end, "Hello") != end)
while (++it != end && *it != "James")
++count;
std::cout << count;
Try this:
below the line
nextword.append(1, c);
add
continue;