Here is my code, not sure why no error but return nothing. it is OK if there is no getline function in ReadFile?? And when debugging, another potential problem is whether the map is needed to define the size when map is declared. I am a beginner. any help is appreciated.
#include <iostream>
#include <fstream>
#include<map>
#include<set>
#include<string>
void swap(char &ch1, char &ch2){ //swap the content
char tmp=ch1;
ch1=ch2;
ch2=tmp;
}
std::string ToLower(std::string s){
for(int i=0;i < s.length();i++)
{
if(s[i]<='Z' && s[i]>='A')
{
s[i]-='A'-'a';
}
}
return s;
}
std::string signature(std::string s)
{
s=ToLower(s);
for(int i=0;i<s.length();i++)
{ int minIndex=i;
for(int j=i+1;j<s.length();j++)
if(s[j]<s[minIndex]) minIndex=j;
swap(s[minIndex],s[i]);
}
return s;
}
void ReadFile(std::ifstream &in, std::map<std::string,std::set<std::string>> &m)
{
while(true)
{
std::string word;
in>>word;
if(!in.good())break;
m[signature(word)].insert(word);
}
}
typedef std::map<std::string, std::set<std::string>>::const_iterator MapIterator;
typedef std::set<std::string>::const_iterator SetIterator;
int main(){
std::ifstream in("ospd.txt");
std::map<std::string, std::set<std::string>> m;
ReadFile(in,m);
for (MapIterator iter = m.begin(); iter != m.end(); iter++)
{
std::cout << "Key: " << iter->first << std::endl << "Values:" << std::endl;
for (SetIterator set_iter = iter->second.begin(); set_iter != iter->second.end(); set_iter++)
std::cout << " " << *set_iter <<std:: endl;
}
system("pause");
return 0;}
Updated: the programming is working. Thanks for everybody!!!
Can you try this.
#include <iterator>
using namespace std;
void ReadFile(std::ifstream &in, std::map<std::string,std::set<std::string>> &m)
istream_iterator<string> itr(in);
istream_iterator<string> end;
while(itr != end)
{
string work = *itr;
m[signature(word)].insert(word);
++itr;
}
Related
I have a txt file which contains two txt file references ei. main.txt contains eg1.txt and eg2.txt and i have to access the content in them and find the occurences of every word and return a string with the word and the documents it was preasent in(0 being eg1.txt and 1 being eg2.txt). My program compiles but I can't get past the first word I encounter. It gives the right result (word: 0 1) since the word is preasent in both the files and in the first position but it doesn't return the other words. Could someone please help me find the error? Thank you
string func(string filename) {
map<string, set<int> > invInd;
string line, word;
int fileNum = 0;
ifstream list (filename, ifstream::in);
while (!list.eof()) {
string fileName;
getline(list, fileName);
ifstream input_file(fileName, ifstream::in); //function to iterate through file
if (input_file.is_open()) {
while (getline(input_file, line)) {
stringstream ss(line);
while (ss >> word) {
if (invInd.find(word) != invInd.end()) {
set<int>&s_ref = invInd[word];
s_ref.insert(fileNum);
}
else {
set<int> s;
s.insert(fileNum);
invInd.insert(make_pair<string, set<int> >(string(word) , s));
}
}
}
input_file.close();
}
fileNum++;
}
Basically your function works. It is a little bit complicated, but i works.
After removing some syntax errors, the main problem is, that you do return nothing from you function. There is also no output statement.
Let me show you you the corrected function which shows some output.
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <set>
#include <sstream>
#include <utility>
using namespace std;
void func(string filename) {
map<string, set<int> > invInd;
string line, word;
int fileNum = 0;
ifstream list(filename, ifstream::in);
while (!list.eof()) {
string fileName;
getline(list, fileName);
ifstream input_file(fileName, ifstream::in); //function to iterate through file
if (input_file.is_open()) {
while (getline(input_file, line)) {
stringstream ss(line);
while (ss >> word) {
if (invInd.find(word) != invInd.end()) {
set<int>& s_ref = invInd[word];
s_ref.insert(fileNum);
}
else {
set<int> s;
s.insert(fileNum);
invInd.insert(make_pair(string(word), s));
}
}
}
input_file.close();
}
fileNum++;
}
// Show the output
for (const auto& [word, fileNumbers] : invInd) {
std::cout << word << " : ";
for (const int fileNumber : fileNumbers) std::cout << fileNumber << ' ';
std::cout << '\n';
}
return;
}
int main() {
func("files.txt");
}
This works, I tested it. But maybe you want to return the findings to your main function. Then you should write:
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <set>
#include <sstream>
#include <utility>
using namespace std;
map<string, set<int> > func(string filename) {
map<string, set<int> > invInd;
string line, word;
int fileNum = 0;
ifstream list(filename, ifstream::in);
while (!list.eof()) {
string fileName;
getline(list, fileName);
ifstream input_file(fileName, ifstream::in); //function to iterate through file
if (input_file.is_open()) {
while (getline(input_file, line)) {
stringstream ss(line);
while (ss >> word) {
if (invInd.find(word) != invInd.end()) {
set<int>& s_ref = invInd[word];
s_ref.insert(fileNum);
}
else {
set<int> s;
s.insert(fileNum);
invInd.insert(make_pair(string(word), s));
}
}
}
input_file.close();
}
fileNum++;
}
return invInd;
}
int main() {
map<string, set<int>> data = func("files.txt");
// Show the output
for (const auto& [word, fileNumbers] : data) {
std::cout << word << " : ";
for (const int fileNumber : fileNumbers) std::cout << fileNumber << ' ';
std::cout << '\n';
}
}
Please enable C++17 in your compiler.
And please see below a brushed up solution. A little bit cleaner and compacter, with comments and better variable names.
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <set>
#include <sstream>
#include <utility>
using WordFileIndicator = std::map<std::string, std::set<int>>;
WordFileIndicator getWordsWithFiles(const std::string& fileNameForFileLists) {
// Here will stor the resulting output
WordFileIndicator wordFileIndicator{};
// Open the file and check, if it could be opened
if (std::ifstream istreamForFileList{ fileNameForFileLists }; istreamForFileList) {
// File number Reference
int fileNumber{};
// Read all filenames from the list of filenames
for (std::string fileName{}; std::getline(istreamForFileList, fileName) and not fileName.empty();) {
// Open the files to read their content. Check, if the file could be opened
if (std::ifstream ifs{ fileName }; ifs) {
// Add word and associated file number to set
for (std::string word{}; ifs >> word; )
wordFileIndicator[word].insert(fileNumber);
}
else std::cerr << "\n*** Error: Could not open '" << fileName << "'\n\n";
// Continue with next file
++fileNumber;
}
}
else std::cerr << "\n*** Error: Could not open '" << fileNameForFileLists << "'\n\n";
return wordFileIndicator;
}
// Some test code
int main() {
// Get result. All words and in which file they exists
WordFileIndicator data = getWordsWithFiles("files.txt");
// Show the output
for (const auto& [word, fileNumbers] : data) {
std::cout << word << " : ";
for (const int fileNumber : fileNumbers) std::cout << fileNumber << ' ';
std::cout << '\n';
}
}
There would be a much faster solution by using std::unordered_map and std::unordered_set
Please make sure your code is composed from many small functions. This improves readability, it easier to reason what code does, in such form parts of code can be reused in alternative context.
Here is demo how it can looks like and why it is better to have small functions:
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <string>
#include <unordered_map>
#include <vector>
struct FileData
{
std::filesystem::path path;
int index;
};
bool operator==(const FileData& a, const FileData& b)
{
return a.index == b.index && a.path == b.path;
}
bool operator!=(const FileData& a, const FileData& b)
{
return !(a == b);
}
using WordLocations = std::unordered_map<std::string, std::vector<FileData>>;
template<typename T>
void mergeWordsFrom(WordLocations& loc, const FileData& fileData, T b, T e)
{
for (; b != e; ++b)
{
auto& v = loc[*b];
if (v.empty() || v.back() != fileData)
v.push_back(fileData);
}
}
void mergeWordsFrom(WordLocations& loc, const FileData& fileData, std::istream& in)
{
return mergeWordsFrom(loc, fileData, std::istream_iterator<std::string>{in}, {});
}
void mergeWordsFrom(WordLocations& loc, const FileData& fileData)
{
std::ifstream f{fileData.path};
return mergeWordsFrom(loc, fileData, f);
}
template<typename T>
WordLocations wordLocationsFromFileList(T b, T e)
{
WordLocations loc;
FileData fileData{{}, 0};
for (; b != e; ++b)
{
++fileData.index;
fileData.path = *b;
mergeWordsFrom(loc, fileData);
}
return loc;
}
WordLocations wordLocationsFromFileList(std::istream& in)
{
return wordLocationsFromFileList(std::istream_iterator<std::filesystem::path>{in}, {});
}
WordLocations wordLocationsFromFileList(const std::filesystem::path& p)
{
std::ifstream f{p};
f.exceptions(std::ifstream::badbit);
return wordLocationsFromFileList(f);
}
void printLocations(std::ostream& out, const WordLocations& locations)
{
for (auto& [word, filesData] : locations)
{
out << std::setw(10) << word << ": ";
for (auto& file : filesData)
{
out << std::setw(3) << file.index << ':' << file.path << ", ";
}
out << '\n';
}
}
int main()
{
auto locations = wordLocationsFromFileList("files.txt");
printLocations(std::cout, locations);
}
https://wandbox.org/permlink/nBbqYV986EsqvN3t
User inputs ints and strings and program stores them into two separate lists in C++.
I get an error on if (isNum(input)) - invalid arguments; could not convert input from char to string.
#include <list>
#include <iostream>
using namespace std;
bool isNum(string s)
{
for (int i = 0; i < s.length(); i++)
if (isdigit(s[i]) == false)
return false;
return true;
}
int main(int argv, char* argc[])
{
int i;
string str;
list<int> l;
list<string> s;
char input;
do {
cin >> input;
if (isNum(input))
{
i = input;
l.push_front(i);
}
else
{
str = input;
s.push_back(str);
}
l.push_back(i);
s.push_back(str);
} while (i != 0);
l.sort();
list<int>::const_iterator iter;
for (iter = l.begin(); iter != l.end(); iter++)
cout << (*iter) << endl;
}
That's not that easy ... But I'd let the stream do the work of deciding if it's an int or a string:
#include <list>
#include <string>
#include <iostream>
int main() // no parameters required for our program
{
std::list<std::string> strings;
std::list<int> numbers;
int num;
do {
std::string str;
if (std::cin >> num) { // try to read an integer if that fails, num will be 0
numbers.push_front(num);
}
else if (std::cin.clear(), std::cin >> str) { // reset the streams flags because
strings.push_front(str); // we only get here if trying to
num = 42; // no, don't exit // extract an integer failed.
}
} while (std::cin && num != 0); // as long as the stream is good and num not 0
strings.sort();
std::cout << "\nStrings:\n";
for (auto const &s : strings)
std::cout << s << '\n';
std::cout.put('\n');
numbers.sort();
std::cout << "Numbers:\n";
for (auto const &n : numbers)
std::cout << n << '\n';
std::cout.put('\n');
}
Sample Output:
foo
5
bar
3
aleph
2
aga
1
0
Strings:
aga
aleph
bar
foo
Numbers:
0
1
2
3
5
Just a few things about your code:
Avoid using namespace std; because it spills out the entire namespace std into the global namespace.
Declare your variables as close to where they're needed.
Use range-based for-loops where possible. Easier to write, easier to read.
I'm writing a program that takes all the file names in a directory and puts them in an array. The problem i'm having is the operator++() shows an error and won't increment the iterator. Any help would be greatly appreciated.
#include <iostream>
#include <string>
#include <filesystem>
namespace fs = std::experimental::filesystem;
int main()
{
std::cout << "Select a directory :";
std::string path;
std::cin >> path;
std::cout << "How many files :";
int dirFiles;
std::cin >> dirFiles;
int i = { 0 };
std::vector<std::string> fileNames(dirFiles);
for (auto& p : fs::directory_iterator(path)){
while (i < dirFiles) {
fileNames[i] = p.path().string();
fs::directory_iterator& operator++();
std::cout << fileNames[i];
i++;
}
}
system("pause");
return 0;
}
directory_iterator already knows how to loop over its constituent elements. You do not need to do additional work yourself:
std::vector<std::string> fileNames;
for (auto& p : fs::directory_iterator(path)){
fileNames.push_back(p.path().string());
}
At the end of the following code I obtain the output and print it on the terminal using cout (at line 60). However, I would like to print it in a text file but I cannot use fprintf in this case.
How could I do it?
This is my code:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <algorithm>
using namespace std;
bool isnotdigit(char c)
{
return !isdigit(c);
}
bool compare(const string s1, const string s2)
{
auto itr1 = s1.begin(), itr2 = s2.begin();
if (isdigit(s1[0]) && isdigit(s2[0]))
{
int n1, n2;
stringstream ss(s1);
ss >> n1;
ss.clear();
ss.str(s2);
ss >> n2;
if (n1 != n2)
return n1 < n2;
itr1 = find_if(s1.begin(), s1.end(), isnotdigit);
itr2 = find_if(s2.begin(), s2.end(), isnotdigit);
}
return lexicographical_compare(itr1, s1.end(), itr2, s2.end());
}
int main()
{
char out_file_name[500];
snprintf(out_file_name, sizeof(out_file_name), "sort.txt");
FILE *out_file;
out_file = fopen(out_file_name, "w");
cout << "Making output file: " << out_file_name << endl;
ifstream in("mydata.txt");
if (in)
{
vector<string> lines;
string line;
while (getline(in, line))
lines.push_back(line);
sort(lines.begin(), lines.end(), compare);
for (auto itr = lines.begin(); itr != lines.end(); ++itr)
cout << *itr << endl;
//fprintf(out_file,);
}
fclose(out_file);
cout << "Output complete" << endl;
return 0;
}
Use std::ofstream and create a file variable:
std::ofstream output_file("output.txt");
if (output_file)
{
output_file << "Hello there.\n";
output_file.flush();
}
Please review your favorite C++ reference in the section about file I/O.
This is one of those areas that differs from the C language.
I am trying to use freopen() to print to a text file and the screen, but I am only achieving the printing to a file.
I was wondering if there was an easy to save the programs output to a file and print it to the screen? Because I had this working another way, but I ended up having to print out every statement twice. One being for the file the other just for the output.
Note: I am new to C++ and I am trying to learn it for a class next semester so direct answer are needed as I have already look online and couldn't find any simple answers to this solution besides.
Here is what I have so far:
#include<iostream>
#include<time.h>
#include<stdlib.h>
#include<fstream>
using namespace std;
void menu(){
cout << "\t********************************************************\n"
<< "\t* Welcome to slot machine. *\n"
<< "\t* Would you like to play? (1 to play, 2 not to play) *\n"
<< "\t********************************************************\n\n";
return;
}
void update(int arr[], int &token) {
if (arr[0]==arr[1] && arr[1]==arr[2]) {
token+=4;
cout << "You win\n\n";
} else if (arr[0]==arr[1] || arr[1]==arr[2] || arr[0]==arr[2]) {
token+=1;
cout << "You got two out of three\n\n";
} else {
token-=1;
cout << "You lose\n\n";
}
}
int main() {
freopen("file.txt", "w", stdout);
int x, arr[3], token=4;
srand(time(0));
menu();
cin >> x;
while(token!=0) {
cout << "You have " << token << " tokens\n\n"
<< "Pull? (1 to pull, 2 not to pull)\n\n";
cin>>x;
if(x==1) {
for(int i=0; i<3; i++) {
arr[i]=1+rand()%10;
}
cout << "\t\t";
for(int j=0; j<3; j++) {
cout << arr[j] << " ";
}
cout << "\n\n";
update(arr,token);
}
else{
cout << "OK\n";
}
}
cin.get();
return 0;
}
I don't know a simple way to achieve that, but I've managed to solve this somehow.
Using fstreams you can output to file the same way you can write to console.
#include <fstream>
int main()
{
std::ofstream f("file.txt");
f << "something";
}
Now there's a point we can start: is there a way we can output to the console and file simultaneously?
I've recently written stream demultiplexer to address that problem:
#include <vector>
#include <ostream>
class stream_demultiplexer
{
private:
typedef std::vector<std::ostream*> str_cont;
str_cont d;
public:
stream_demultiplexer& put(std::ostream::char_type ch)
{
for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
(*it)->put(ch);
return *this;
}
stream_demultiplexer& write(const std::ostream::char_type* s, std::streamsize count)
{
for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
(*it)->write(s, count);
return *this;
}
stream_demultiplexer& flush()
{
for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
(*it)->flush();
return *this;
}
template<typename T>
stream_demultiplexer& operator<<( const T& obj )
{
for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
(**it) << obj;
return *this;
}
stream_demultiplexer& operator<<(std::ios_base& (*func)(std::ios_base&))
{
for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
(**it) << func;
return *this;
}
template<typename CharT, typename Traits>
stream_demultiplexer& operator<<(std::basic_ios<CharT,Traits>& (*func)(std::basic_ios<CharT,Traits>&) )
{
for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
(**it) << func;
return *this;
}
stream_demultiplexer& operator<<(std::ostream& (*func)(std::ostream&) )
{
for(str_cont::iterator it = d.begin(); it != d.end(); ++it)
(**it) << func;
return *this;
}
void add_stream(std::ostream& ss)
{
d.push_back(&ss);
}
};
You can use it like this:
stream_demultiplexer spl;
std::ofstream f("file.txt");
spl.add_stream(f);
spl.add_stream(std::cout);
spl << 55 << " HELLO WORLD";
My approach has advantage that manipulators and unformatted output works correctly:
spl << 76 << " " << std::hex << 76 << std::endl;
spl.put('a');
spl.write("ABCDE", 5);
The easy way in a UNIX-like environment is to use the shell command tee:
$ my-program | tee output.txt
will copy stdout to the terminal, and also to the file output.txt.
If you have to do it in code, you could use your own output stream instead of cout, which forwards every operator<< to two (or more) ostreams. This feels nicer (to me) than mucking around with the C output file underlying the C++ ostream cout.
#include <ostream>
class Tee {
std::ostream &first, &second;
template<typename T> friend Tee& operator<< (Tee&, T);
public:
Tee(std::ostream &f, std::ostream &s) : first(f), second(s) {}
};
template <typename T>
Tee& operator<< (Tee &t, T val)
{
t.first << val;
t.second << val;
return t;
}
Then, if you replace your freopen line with:
std::ofstream outfile("file.txt");
Tee tee(std::cout, outfile);
you can just use tee << instead of cout <<.
Note that you'll either need to pass tee into your functions, or make it a global for that to work.