I have a strange issue with the set_differences function from the <algorithm> header. I am attempting to read two text files line by line and each line is put into a corresponding set. For some reason set-difference does not detect any difference between the two sets though there most certainly are. When I hardcode populate two sets it works just fine. It must be something with the content of the strings read from the files but I can't figure out what.
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <set>
#include <sstream>
#include <algorithm>
#include <iterator>
#include <string>
std::set<std::string> listOfFiles(std::string fileName) {
std::ifstream tempFile;
tempFile.open(fileName.c_str());
std::string X;
std::set<std::string> fileNameSet;
if (tempFile.is_open()) {
while (std::getline(tempFile, X)) {
fileNameSet.insert(X);
}
}
else {
std::cerr << "Failed to open " + fileName + "\n";
exit(0);
}
return fileNameSet;
}
std::set<std::string> setDifferences(std::set<std::string> a, std::set<std::string> b) {
using namespace std;
set<string> result;
set_difference( a.begin(), a.end(), b.begin(), b.end(), inserter(result, result.begin()));
cout << "Difference" << endl << "-------------" << endl;
for (set<string>::const_iterator i = result.begin(); i != result.end(); ++i) {
cout << *i << endl;
}
return result;
}
int main()
{
std::set<std::string> initialSet = listOfFiles("test1.txt");
std::set<std::string> afterNGD = listOfFiles("test2.txt");
std::set<std::string> result, result2;
std::set<std::string> a, b;
a.insert("one");
a.insert("two");
a.insert("three");
b.insert("a");
b.insert("b");
b.insert("three");
//Fails: result is empty
result = setDifferences(initialSet, afterNGD);
//Pass: result2 constains strings "one" and "two"
result2 = setDifferences(a,b);
return 0;
}
My text files contain:
test1.txt
.Xil
fileNames.txt
hostlistfile.txt
ipcore_dir
iseconfig
item.prj
item.sch
item.sym
item.syr
item.ucf
item.vhf
item.xdl
item.xst
item_bitgen.xwbt
item_guide.ncd
item_summary.html
pa.fromNcd.tcl
planAhead.ngc2edif.log
planAhead_pid11956.debug
planAhead_pid1272.debug
planAhead_pid16492.debug
planAhead_pid19040.debug
planAhead_pid7804.debug
planAhead_pid9888.debug
planAhead_run_1
planAhead_run_2
sch2HdlBatchFile
SingleItemTest.gise
SingleItemTest.tcl
SingleItemTest.xise
template files
templates
xst
_xmsgs
text2.txt:
.Xil
fileNames.txt
hostlistfile.txt
ipcore_dir
iseconfig
item.bld
item.lso
item.ngc
item.ngd
item.ngr
item.prj
item.sch
item.sym
item.syr
item.ucf
item.vhf
item.xdl
item.xst
item_bitgen.xwbt
item_guide.ncd
item_ngdbuild.xrpt
item_summary.html
item_vhdl.prj
item_xst.xrpt
pa.fromNcd.tcl
planAhead.ngc2edif.log
planAhead_pid11956.debug
planAhead_pid1272.debug
planAhead_pid16492.debug
planAhead_pid19040.debug
planAhead_pid7804.debug
planAhead_pid9888.debug
planAhead_run_1
planAhead_run_2
sch2HdlBatchFile
SingleItemTest.gise
SingleItemTest.tcl
SingleItemTest.xise
template files
templates
xlnx_auto_0_xdb
xst
_ngo
_xmsgs
From the reference:
Copies the elements from the sorted range [first1, last1) which are
not found in the sorted range [first2, last2) to the range beginning
at d_first.
From your example files, all the elements in text1.txt can be found in text2.txt, so the output is as expected.
Related
I'm writing a code that counts the number of occurrences of each word in a file, and print out these words in order of number of occurrences. After each word, it prints its number of occurrences. Words occurring the same number of times in the file are listed in alphabetical order.
I don't know how to modify that code to get the words in order of number of occurrences, and that words occurring the same number of times in the file are listed in alphabetical order.
Limitations: I can use only headers like <iostream>, <map>, <string>, <fstream> and <utility>
Example of how it should work:
In:
one two three one two four two one two
Out:
four 1
three 1
one 3
two 4
By now, I've done something like that:
#include <iostream>
#include <fstream>
#include <map>
#include <string>
typedef std::map<std::string, int> StringIntMap;
void count_words(std::istream &in, StringIntMap &words)
{
std::string text;
while (in >> text)
{
++words[text];
}
}
int main(int argc, char **argv)
{
std::ifstream in("readme.txt");
StringIntMap words_map;
count_words(in, words_map);
for (StringIntMap::iterator it = words_map.begin(); it != words_map.end(); ++it)
{
std::cout << it->first << " " << it->second << std::endl;
}
}
A solution using std::map to perform the sorting.
Readability may be further improved by replacing std::pair<string, int> with a struct with a meaningful name.
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <utility>
using std::cout;
using std::ifstream;
using std::map;
using std::pair;
using std::string;
struct Dummy {};
struct Compare {
bool operator()(const pair<string, int> &p1,
const pair<string, int> &p2) const {
if (p1.second < p2.second) {
return true;
} else if (p1.second > p2.second) {
return false;
} else {
return p1.first < p2.first;
}
}
};
int main(int argc, char **argv) {
ifstream in("readme.txt");
map<string, int> occurences;
string word;
while (in >> word) {
occurences[word]++;
}
map<pair<string, int>, Dummy, Compare> sorted;
for (const auto &p : occurences) {
sorted.insert({p, Dummy{}});
}
for (const auto &p : sorted) {
cout << p.first.first << ": " << p.first.second << "\n";
}
}
you can use std::multimap to do the the sort.
int main()
{
std::map<std::string,int> words_map;
count_words(std::cin, words_map);
std::multimap<int,std::string> frequency_map;
for(auto& [word,freq]:words_map){
frequency_map.insert({freq,word});
}
for(auto& [freq,word]:frequency_map){
std::cout << word << ' ' << freq << '\n';
}
}
https://godbolt.org/z/Er7o8Wec1
I am using STL map in C++ for counting the frequency of words in a text file and words must be sort in lexicographic order. Input data is given as a text file. Ive already read and added them in map but i got a problem with sorting.
Example, i have { "Abc", "abc", "bag", "Boom", "great"}. When i added them in map, i got
Abc 1 Boom 1 abc 1 bag 1 great 1
but expected result is
Abc 1 abc 1 Boom 1 bag 1 great 1
#include <iostream>
#include <cstring>
#include <map>
#include <fstream>
using namespace std;
typedef map<string, int> word_count;
int main(){
word_count wc;
fstream f_in;
f_in.open("test.in");
string x;
while( !f_in.eof()){
f_in >> x;
wc[x]++;
}
f_in.close();
return 0;
}
Here is my code for reading input. Any help for my problem? Thanks
The OP wants a custom sort order that's subtly different from the standard lexicographical order. A map with a custom sort order can be achieved by passing in a custom Compare (Compare is the third template parameter of map):
#include <algorithm>
#include <cctype>
#include <cstring>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <vector>
using std::string;
using std::transform;
using std::map;
using std::cout;
struct Compare {
bool operator() (const string& s0, const string& s1) const {
// construct all lowercase versions of s0 and s1
string str0(s0.length(),' ');
string str1(s1.length(),' ');
transform(s0.begin(), s0.end(), str0.begin(), tolower);
transform(s1.begin(), s1.end(), str1.begin(), tolower);
if (!str0.empty() and !str1.empty() and str0.front()==str1.front()) {
// do a standard lexicographic sort if the first character is the same
return s0 < s1;
}
else {
// otherwise, do a case-insensitive lexicographic sort using the lowercased strings
return str0 < str1;
}
}
};
typedef map<string, int, Compare> word_count;
int main(){
word_count wc;
auto words = { "Abc", "abc", "bag", "Boom", "great"};
for (auto word : words)
wc[word]++;
for(auto elem : wc)
cout << elem.first << " " << elem.second << '\n';
return 0;
}
This indeed produces the desired output:
Abc 1
abc 1
Boom 1
bag 1
great 1
Try out a live version of the code online
By default, the third template parameter of a map is less<key> (in this case, less<string>), which will sort strings in the standard lexicographical A-z order.
Here is a complete example with file reading included, and using the base sorting functionality of std::map.
#include <iostream>
#include <cstring>
#include <map>
#include <fstream>
typedef std::map<std::string, int> word_count;
int main(int argc, char** argv){
if(argc < 2){
std::cout << "Please provide a file name." << std::endl;
return 1;
}
word_count wc;
std::ifstream inputfile(argv[1]);
if (inputfile.is_open()){
std::string x;
while(inputfile >> x){
wc[x]++;
}
inputfile.close();
}else {std::cout << "Program aborted: unable to open input file" << std::endl; return 1;}
for(auto word: wc){
std::cout << word.first << "\t" << word.second << std::endl;
}
return 0;
}
In the code i am working on now I have a vector load itself from a txt file now I was trying to see if their was a way to replace certain words in the vector without needing a position or anything
so for example if the txt contained a list of animals and i wanted to change bird to book how would i do that without need the position of the letters
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
using namespace std;
vector <string> test;
int main()
{
string file;
fstream fout( "Vector.txt" );
while ( !fout.eof())
{
getline(fout,file);
test.push_back(file);
}
fout.close();
for( int i = 0; i < test.size(); i++)
{
cout << test[i] << endl;
}
system("pause");
}
txt contains:
dog
cat
bird
hippo
wolf
Use std::transform().
std::string bird2book(const string &str)
{
if (str == "bird")
return "book";
return str;
}
std::transform(test.begin(), test.end(), test.begin(), bird2book);
you can use std::replace
std::replace (test.begin(), test.end(), "bird", "book");
Try this:
typedef std::istream_iterator<string> isitr;
ifstream fin("Vector.txt");
vector <string> test{ isitr{fin}, isitr{} }; // vector contains strings
map<string,string> dict{ // replacements dictionary
{"bird", "book"}, {"cat", "kitten"}
};
for(auto& x: test) // x must be a reference
{
auto itr = dict.find(x);
if(itr != dict.end()) // if a match was found
x = itr->second; // replace x with the found replacement
// (this is why x must be a reference)
}
for(const auto& x: test)
cout << test << " ";
Use STL!! It's our power. Everything you need:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
#include <string>
#include <fstream>
#include <map>
int main()
{
std::vector<std::string> words;
const std::map<std::string, std::string> words_to_replace{
{ "bird", "book" }, { "cat", "table" }
};
auto end = words_to_replace.cend();
std::transform(
std::istream_iterator<std::string>{ std::ifstream{ "file.txt" } },
std::istream_iterator<std::string>(),
std::back_inserter(words),
[&](const std::string& word) {
auto word_pos = words_to_replace.find(word);
return (word_pos != end) ? word_pos->second : word;
});
std::copy(words.cbegin(), words.cend(),
std::ostream_iterator<std::string>(std::cout, "\n"));
std::cout << std::endl;
}
I wrote a program which creates comma separated string out of vector of int like below:
std::vector<unsigned int> Vec;
Vec.push_back(50);
Vec.push_back(60);
Vec.push_back(10);
Vec.push_back(20);
Vec.push_back(30);
Vec.push_back(2);
Vec.push_back(1);
std::stringstream lineNumString;
lineNumString.str(std::string());
lineNumString.clear();
std::copy(Vec.begin(), Vec.end(), std::ostream_iterator<unsigned int>(lineNumString, ","));
std::string lineString(lineNumString.str());
lineString = lineString.substr(0, lineString.length()-1);
std::cout << std::endl << lineString;
If you see output of above program is:
`50,60,10,20,30,2,1`
But I want to change my output in some different format. I want to have maximum THREE numbers on one line and next numbers to next line. Like below:
50,60,10,
20,30,2,
1
I tried creating child vectors from Vec and tried using them to create different strings. Then I tried splitting lineString and then used those strings.
Please let me know if there is any better way to achieve final output?
I am using VS2010. I can use BOOST features also.
Thanks.
The classic loop way:
#include <vector>
#include <iostream>
#include <sstream>
#include <iterator>
int main()
{
std::vector<unsigned int> Vec;
Vec.push_back(50);
Vec.push_back(60);
Vec.push_back(10);
Vec.push_back(20);
Vec.push_back(30);
Vec.push_back(2);
Vec.push_back(1);
for(auto it = Vec.begin(); it != Vec.end();)
{
std::cout << *it++;
if(it != Vec.end())
std::cout << ",";
if((it - Vec.begin()) % 3 == 0)
std::cout << std::endl;
}
return 0;
}
The Boost.spirit way:
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <iostream>
#include <vector>
#include <string>
namespace karma = boost::spirit::karma;
int main()
{
std::vector<unsigned int> v = {50, 60, 10, 20, 30, 2, 1};
using karma::int_;
using karma::eol;
using karma::duplicate;
using karma::generate;
std::string out;
std::back_insert_iterator<std::string> it(out);
generate(it, int_ << "," << *(int_ << "," << int_ << "," << duplicate[ &int_ << eol << int_ << ","]), v);
out[out.size() - 1] = 0;
std::cout << out << std::endl;
return 0;
}
The little trick with duplicate: it duplicates attribute int_, which is mandatory, because predicate & consume it, in order to know if we display the end of the line (eol) or not.
I am trying to sort an array of strings, but it's not sorting anything.... what am I doing wrong?
string namesS[MAX_NAMES];
int compare (const void * a, const void * b){
return ( *(char*)a - *(char*)b );
}
void sortNames(){
qsort(namesS, MAX_NAMES, sizeof(string), compare);
}
This is C++, not C. Sorting an array of strings is easy.
#include <string>
#include <vector>
#include <algorithm>
std::vector<std::string> stringarray;
std::sort(stringarray.begin(), stringarray.end());
std::qsort is inherited from the standard C library. It will not work.
You need to use std::sort for sorting strings.
Specifically, cast std::string to void* and then to char* is undefined and won't work.
algorithm sort in CPP has the same complexity as qsort:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
bool compare(string a, string b){
cout << "compare(" << a << "," << b << ")" << endl;
return (a.compare(b) < 0);
}
int main () {
string mystrs[] = {"www","ggg","bbb","ssss","aaa"};
vector<string> myvector (mystrs, mystrs + 5);
vector<string>::iterator it;
sort (myvector.begin(), myvector.end(), compare);
cout << "vector contains:";
for (it=myvector.begin(); it!=myvector.end(); ++it)
cout << " " << *it;
cout << endl;
return 0;
}
You can use boost::sort, like this:
#include <vector>
#include <boost/range/algorithm.hpp>
std::vector<std::string> stringarray;
boost::sort(stringarray);
If you want use find use boost::find, like this:
std::string findme;
auto offset = boost::find(stringarray, findme) - stringarray.begin()
See 2 useful functions (m_stringarray should be member of ClassA):
const size_t ClassA::GetIdByName(std::string name) const
{
return (boost::find(this->m_stringarray, name) - this->m_stringarray.begin());
}
const std::string ClassA::GetNameById(size_t id) const
{
return this->m_stringarray[id];
}
As many here have stated, you could use std::sort to sort, but what is going to happen when you, for instance, want to sort from z-a? This code may be useful
bool cmp(string a, string b)
{
if(a.compare(b) > 0)
return true;
else
return false;
}
int main()
{
string words[] = {"this", "a", "test", "is"};
int length = sizeof(words) / sizeof(string);
sort(words, words + length, cmp);
for(int i = 0; i < length; i++)
cout << words[i] << " ";
cout << endl;
// output will be: this test is a
}
If you want to reverse the order of sorting just modify the sign in the cmp function.
Here is C++ another way to sort array of string without using <vector>.
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
string WordArray[] = {"AA","DD","CC","BB","ZZ","NN"};
sort(begin(WordArray), end(WordArray)); /*Sort the Array*/
for(auto& Word: WordArray){
cout<<Word<<endl; /*Print Every String Element*/
}
return 0;
}