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;
}
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
Why does set.count('a') output 1 when there are 3 a's?
Program:
bool isAnagram(string s, string t) {
unordered_set<char> set;
for(int i=0; i<s.size(); i++){
set.insert(s[i]);
}
cout << endl << set.count('a') << endl;
return false;
}
Input:
s = 'anagram'
Output:
1
There's only one a in the set. If you want multiple as you need to use a multiset.
Example:
#include <iostream>
#include <set>
#include <string>
size_t count_char(const std::string& s, char ch) {
// fill the set directly using the strings begin and end iterators
std::multiset<char> set(s.begin(), s.end());
return set.count(ch);
}
int main() {
std::cout << count_char("anagram", 'a') << '\n';
}
Output:
3
You have to specify the range in count function :
count (InputIterator first, InputIterator last, const T& val)
Example:
#include <iostream>
#include <algorithm>
using namespace std;
int main(){
string s= "anagram";
cout << count(s.begin(), s.end(), 'a') << endl;
return 0;
}
Output:
3
I am trying to open a text file and then rearrange it in descending order, to show who has the highest score. In the text file there's the player name and their score.
I've managed to print out the textfile in c++, but I cannot find a way to sort it since the variables are in the text file.
#include <string>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <functional>
using namespace std;
struct player {
string name;
int score;
int position;
};
int main()
{
string line;
ifstream inFile;
inFile.open("C:/Users/kkpet/Desktop/highscore.txt");
if (inFile.is_open()) {
while (getline(inFile, line)) {
player x;
ifstream inFile;
inFile.open("C:/Users/kkpet/Desktop/highscore.txt");
cout << line << '\n';
}
inFile.close();
}
else
cout << "Unable to open text";
}
Assuming your text file looks like this:
Name1 1
Name2 1
Name4 5
Name3 6
you could do something like this:
#include <string>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <functional>
#include <vector>
int main()
{
std::string line;
std::ifstream inFile;
inFile.open("/C:/Users/kkpet/Desktop/highscore.txt");
if (inFile.is_open()) {
std::vector<std::pair<int, std::string> > score_vector;
std::string name;
int score;
while (inFile >> name >> score) {
score_vector.push_back(std::make_pair(score, name));
std::cout << line << '\n';
}
inFile.close();
std::sort(score_vector.begin(), score_vector.end());
std::reverse(score_vector.begin(), score_vector.end());
for(auto it = score_vector.begin(); it != score_vector.end(); ++it){
std::cout << "Name: " << it->second << " Score: " << it->first << std::endl;
}
}
else
std::cout << "Unable to open text";
}
You first read the file line by line using inFile << name << score directly gives you the name and score of the player. You then create a pair out of them with score as first, which just makes it easier to sort, you could also sort by the second element of a pair using your own compare function, but for simplicity I put it this way around. Then you can easily sort the vector with the std::sort method. Afterwards it needs to be reverted.
Full code with custom compare function:
#include <string.h>
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <functional>
#include <vector>
// This is a compare funciton for the sort algorithm of std::sort. See [1]
bool compareScorePair(std::pair<std::string, int>&a, std::pair<std::string, int>&b){
if(a.second > b.second){return true;}
if(a.second == b.second){return a.first.compare(b.first) > 0;}
return false;
}
int main()
{
std::ifstream inFile;
inFile.open("C:/Users/kkpet/Desktop/highscore.txt");
if (inFile.is_open()) {
std::vector<std::pair<std::string, int> > score_vector;
std::string name;
int score;
while (inFile >> name >> score) { // Can be used to directly assign istream data to variables. See [2]
score_vector.push_back(std::make_pair(name, score)); // Storing data as pair, to keep relationships between score and name.
}
inFile.close();
std::sort(score_vector.begin(), score_vector.end(), compareScorePair); // Sort the vector with the custom compare function, See top of code.
int place = 1;
//auto is used purely for convenience. auto represents std::vector<std::pair<std::string, int> >::iterator here.
for(auto it = score_vector.begin(); it != score_vector.end(); ++it){
std::cout << "Place: " << place << " Name: " << it->first << " Score: " << it->second << std::endl;
++place;
}
// The whole for loop could look like this:
/*
for(uint i = 0; i < score_vector.size(); ++i){
std::string name_out = score_vector[i].first;
int score_out = score_vector[i].second;
std::cout << "Place: " << i << " Name: " << name_out << " Score: " << score_out << std::endl;
}
*/
}
else
std::cout << "Unable to open text";
}
Output:
Place: 1 Name: Name3 Score: 6
Place: 2 Name: Name4 Score: 5
Place: 3 Name: Name2 Score: 1
Place: 4 Name: Name1 Score: 1
Links:
[1]: https://en.cppreference.com/w/cpp/named_req/Compare
[2]: http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/
Information on pair:
https://en.cppreference.com/w/cpp/utility/pair
Information on iterators (see auto keyword):
https://www.geeksforgeeks.org/iterators-c-stl/
No need to use C++ for this problem! Just enter this in the shell prompt.
sort -rk 2 highscore.txt > sorted_scores.txt
Explanation:
'sort' sorts a file, typically by the first letter.
The -k 2 option means to sort by the second column.
The -r option means to reverse (so highest score is on top).
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.
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;
}