So the file is like this:
tucanos 10
tacobell 5
tucanos 5
pizzahut 15
tucanos 5
pizzahut 2
tucanos 5
Where the string is the restaurant's name and the number is the number of likes it has. I am supposed to find out the sum of number of likes for each restaurant from reading the file but I have no idea how to do it. Does any of you have any hints for me?
first, here is nice function to split std::string into std::vector given a delimiter character:
std::vector<std::string> &split(const std::string &s,
char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
next, we can achieve desired result using this code:
int main(int argc, char** argv) {
std::map<std::string, int> map;
std::ifstream file("file_with_string.txt", std::ifstream::in);
std::string temp;
while (std::getline(file, temp)) {
std::cout << temp << "\n";
std::vector<std::string> tokens;
tokens = split(temp, ' ', tokens);
for (std::vector<std::string>::iterator it = tokens.begin();
it != tokens.end(); it++) {
std::vector<std::string>::iterator it1 = it;
std::map<std::string, int>::iterator mapIt = map.find(*it++);
int number;
std::istringstream(*it) >> number;
if (mapIt != map.end()) {
(mapIt->second) += (number);
} else {
map[*it1] = number;
}
}
}
for (std::map<std::string, int>::iterator it = map.begin();
it != map.end(); it++) {
std::cout << it->first << " " << it->second << "\n";
}
return 0;
}
tucanos 10 tacobell 5 tucanos 5 pizzahut 15 tucanos 5 pizzahut 2 tucanos 5
pizzahut 17
tacobell 5
tucanos 25
RUN SUCCESSFUL (total time: 55ms)
we can make it simpler
std::map<std::string, int> map;
std::ifstream infile("file_with_string.txt",std::ifstream::in);
std::string resturant = "";
int likes = 0;
while(infile >> resturant >> likes ){
map[resturant ] += likes ;
}
but first version gives more insight IMO into iterators, how to traverse std::map, std::vector and how populate map.
Here's some pseudocode:
Open file
For each restaurant-likes pair
Read restaurant-likes pair from file
If first time we've seen a restaurant
Add restaurant to list
Lookup restaurant in list and add likes to total for that restaurant
End For
Close file
Print all restarants and total likes
Use fscanf to parse the input from file and then apply the add operation.
Example
// c:\temp\nameofexecutable.exe < yourtextfile.txt
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
using namespace std;
int main()
{
string resturant;
int likes;
map<string, int> likesCount;
while (cin >> resturant >> likes) {
likesCount[resturant] += likes;
}
for_each(begin(likesCount), end(likesCount), [&](pair<string,int> x){
cout << x.first << " " << x.second << endl;
});
//// using plain for instead of std::for_each
//for (auto i = begin(likesCount); i != end(likesCount); i++) {
// cout << i->first << " " << i->second << endl;
//}
}
maybe can help you:
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <iterator>
int
main() {
std::map<std::string, int> count;
std::ifstream infile("cars.dat", std::ios::in);
if (infile == NULL) {
fprintf(stderr, "Failed to open file.\n");
exit(-1);
}
std::string resturant = "";
int likes = 0;
while(infile >> resturant >> likes ){
count[resturant ] += likes ;
}
printf("--------------------each sum-------------------\n");
std::map<std::string, int>::iterator it(count.begin());
for (; it != sum.end(); ) {
printf("%s %d\n", (it->first).c_str(),it->second);
it++;
}
infile.close();
return 0;
}
The result:
--------------------each sum-------------------
pizzahut 17
tacobell 5
tucanos 25
Related
i have this code and i want to find a word in my vector and delete the item that includes that word but, my code will delete from the first line until the item that i want, how can i fix that?
std::string s;
std::vector<std::string> lines;
while (std::getline(theFile, s))
{
lines.push_back(s);
}
//finding item in vector and changing it
for (unsigned i = 0; i < lines.size(); i++)
{
std::size_t found = lines[i].find(name);
if (found != std::string::npos)
{
lines.erase(lines.begin() + i);
}
}
Update 1:
this is my full Code:
I'm opening a file that contains some elements in this format
( David, 2002 , 1041 , 1957 )
( cleve, 2003 , 1071 , 1517 )
( Ali, 2005 , 1021 , 1937 )
i'm getting a user input and finding the line that contains it. then i want to remove that line completely so i import it to a vector and then i can't modify it
#include <iostream>
#include <string>
#include <vector>
#include <stream>
#include <algorithm>
using namespace std;
using std::vector;
int main(){
string srch;
string line;
fstream Myfile;
string name;
int counter;
Myfile.open("Patientlist.txt", ios::in | ios::out);
cout <<"Deleting your Account";
cout << "\nEnter your ID: ";
cin.ignore();
getline(cin, srch);
if (Myfile.is_open())
{
while (getline(Myfile, line))
{
if (line.find(srch) != string::npos)
{
cout << "\nYour details are: \n"
<< line << endl;
}
break;
}
}
else
{
cout << "\nSearch Failed... Patient not found!" << endl;
}
Myfile.close();
ifstream theFile("Patientlist.txt");
//using vectors to store value of file
std::string s;
std::vector<std::string> lines;
while (std::getline(theFile, s))
{
lines.push_back(s);
}
//finding item in vector and changing it
for (unsigned i = 0; i < lines.size(); i++)
{
std::size_t found = lines[i].find(name);
if (found != std::string::npos)
{
lines.erase(lines.begin() + i);
}
}
//writing new vector on file
ofstream file;
file.open("Patientlist.txt");
for (int i = 0; i < lines.size(); ++i)
{
file << lines[i] << endl;
}
file.close();
cout << "Done!";
}
The erasing loop is broken. The proper way is to use iterators and use the iterator returned by erase. Like so:
// finding item in vector and changing it
for (auto it = lines.begin(); it != lines.end();) {
if (it->find(name) != std::string::npos) {
it = lines.erase(it);
} else {
++it;
}
}
Or using the erase–remove idiom:
lines.erase(std::remove_if(lines.begin(), lines.end(),
[&name](const std::string& line) {
return line.find(name) != std::string::npos;
}), lines.end());
Or since C++20 std::erase_if(std::vector):
std::erase_if(lines, [&name](const std::string& line) {
return line.find(name) != std::string::npos;
});
You can use remove_if for this. The predicate argument should check if you can find a word in a line. Then remember to use erase to "shrink" the vector to its new size.
[Demo]
#include <algorithm> // remove_if
#include <iostream> // cout
#include <string>
#include <vector>
int main()
{
std::vector<std::string> lines{"one two three", "three four five", "five six one"};
lines.erase(
std::remove_if(std::begin(lines), std::end(lines), [](auto& line) {
return line.find("one") != std::string::npos;}),
std::end(lines)
);
for (auto& line : lines) { std::cout << line << "\n"; }
}
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 a beginner in c++ and required to write a c++ program to read and print a csv file like this.
DateTime,value1,value2
12/07/16 13:00,3.60,50000
14/07/16 20:00,4.55,3000
May I know how can I proceed with the programming?
I manage to get the date only via a simple multimap code.
I spent some time to make almost (read notice at the end) exact solution for you.
I assume that your program is a console application that receives the original csv-file name as a command line argument.
So see the following code and make required changes if you like:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
std::vector<std::string> getLineFromCSV(std::istream& str, std::map<int, int>& widthMap)
{
std::vector<std::string> result;
std::string line;
std::getline(str, line);
std::stringstream lineStream(line);
std::string cell;
int cellCnt = 0;
while (std::getline(lineStream, cell, ','))
{
result.push_back(cell);
int width = cell.length();
if (width > widthMap[cellCnt])
widthMap[cellCnt] = width;
cellCnt++;
}
return result;
}
int main(int argc, char * argv[])
{
std::vector<std::vector<std::string>> result; // table with data
std::map<int, int> columnWidths; // map to store maximum length (value) of a string in the column (key)
std::ifstream inpfile;
// check file name in the argv[1]
if (argc > 1)
{
inpfile.open(argv[1]);
if (!inpfile.is_open())
{
std::cout << "File " << argv[1] << " cannot be read!" << std::endl;
return 1;
}
}
else
{
std::cout << "Run progran as: " << argv[0] << " input_file.csv" << std::endl;
return 2;
}
// read from file stream line by line
while (inpfile.good())
{
result.push_back(getLineFromCSV(inpfile, columnWidths));
}
// close the file
inpfile.close();
// output the results
std::cout << "Content of the file:" << std::endl;
for (std::vector<std::vector<std::string>>::iterator i = result.begin(); i != result.end(); i++)
{
int rawLen = i->size();
for (int j = 0; j < rawLen; j++)
{
std::cout.width(columnWidths[j]);
std::cout << (*i)[j] << " | ";
}
std::cout << std::endl;
}
return 0;
}
NOTE: Your task is just to replace a vector of vectors (type std::vector<std::vector<std::string>> that are used for result) to a multimap (I hope you understand what should be a key in your solution)
Of course, there are lots of possible solutions for that task (if you open this question and look through the answers you will understand this).
First of all, I propose to consider the following example and to try make your task in the simplest way:
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
using namespace std;
int main()
{
string str = "12/07/16 13:00,3.60,50000";
stringstream ss(str);
vector<string> singleRow;
char ch;
string s = "";
while (ss >> ch)
{
s += ch;
if (ss.peek() == ',' || ss.peek() == EOF )
{
ss.ignore();
singleRow.push_back(s);
s.clear();
}
}
for (vector<string>::iterator i = singleRow.begin(); i != singleRow.end(); i++)
cout << *i << endl;
return 0;
}
I think it can be useful for you.
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 have any sequence (or sentence) and i want to extract the last 2 strings.
For example,
sdfsdfds sdfs dfsd fgsd 3 dsfds should produce: 3 dsfds
sdfsd (dfgdg)gfdg fg 6 gg should produce: 6 gg
You can use std::string::find_last_of function to find spaces.
int main()
{
std::string test = "sdfsdfds sdfs dfsd fgsd 3 dsfds";
size_t found1 = test.find_last_of( " " );
if ( found1 != string::npos ) {
size_t found2 = test.find_last_of( " ", found1-1 );
if ( found2 != string::npos )
std::cout << test.substr(found2+1, found1-found2-1) << std::endl;
std::cout << test.substr(found1+1) << std::endl;
}
return 0;
}
The following will work if your strings are whitespace separated.
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
int main()
{
string str = "jfdf fhfeif shfowejef dhfojfe";
stringstream sstr(str);
vector<string> vstr;
while(sstr >> str)
{
vstr.push_back(str);
}
if (vstr.size() >= 2)
cout << vstr[vstr.size()-2] << ' ';
if (vstr.size())
cout << vstr[vstr.size()-1] << endl;
return 0;
}
Returns the strings in the wrong order, but if that doesn't matter,
std::string s ("some words here");
std::string::size_type j;
for(int i=0; i<2; ++i) {
if((j = s.find_last_of(' ')) == std::string::npos) {
// there aren't two strings, throw, return, or do something else
return 0;
}
std::cout << s.c_str()+j+1;
s = " " + s.substr(0,j);
}
Alternatively,
struct extract_two_words {
friend std::istream& operator>> (std::istream& in , extract_two_words& etw);
std::string word1;
std::string word2;
};
std::istream& operator>> (std::istream& in , extract_two_words& etw) {
std::string str1, str2;
while(in) {
in >> str1;
in >> str2;
}
etw.word2 = str1;
etw.word1 = str2;
}
I would encourage you to have a look at the Boost library. It has algorithms and data structures that help you tremendously. Here's how to solve your problem using Boost.StringAlgo:
#include <boost/algorithm/string/split.hpp>
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::string test = "sdfsdfds sdfs dfsd fgsd 3 dsfds";
std::vector<std::string> v;
boost::algorithm::split(v, test, [](char c) { return c==' ';});
std::cout << "Second to last: " << v.at(v.size()-2) << std::endl;
std::cout << "Last: " << v.at(v.size()-1) << std::endl;
}
I would also encourage you to always use the vector::at method instead of []. This will give you proper error handling.
int main()
{
std::string test = "sdfsdfds sdfs dfsd fgsd 3 dsfds";
size_t pos = test.length();
for (int i=0; i < 2; i++)
pos = test.find_last_of(" ", pos-1);
std::cout << test.substr(pos+1) << std::endl;
}
Simpler :)