I am a complete beginner to c++ and for an assignment I have been asked to sort a text file of superheroes and output them to another text file e.g.
Unsorted.txt
Deadpool_8
Phoenix_9
Toad_4
Jubilee_3
alphabetically and numerically.
I have attempted to use .back for each line of string for the numerical sorting although it simply will not accept it and returns an error (which I have included in the code) as well as, whilst it is happy to write to console in full with cout attempting to write to text file results in only the last line of string e.g.
Toad_4
(all my mistakes have been commented out and it is currently only sorting alphabetically)
I'm paranoid about asking the same question as someone else, but I have not been able to find anything which solves my problem.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <fstream>
#include <limits>
using namespace std;
// Empty vector holding names from file
vector<string> names;
string word;
string number;
string filename;
string sortChoice;
string lastChar;
bool alphaSortFinished = false; //bool added to prevent unnecessary looping
bool sortFinished = false;
void sortNumerically()
{
//word = word.back; returns this error
/*Error 1 error C3867: 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::back': function call missing argument list; use
'&std::basic_string<char,std::char_traits<char>,std::allocator<char>>::back' to create a pointer to member
d:\visual studio 2013\assessment one mdu118\strings, classes assessment one\source.cpp 20 1 Strings, Classes Assessment One*/
cout << "Please specify the file you would like to open\n" << endl;
cin >> filename;
// Read names from specified file
ifstream inFile(filename);
while (!inFile && sortFinished == false)
{
cout << "Unable to open file\n";
inFile.close();
sortNumerically();
}
while (getline(inFile, word)) //get lines of the string, store them in string word;
{
names.push_back(word);
}
sort(names.begin(), names.end());
// Loop to print names
for (size_t i = 0; i < names.size(); i++)
{
//ofstream writeToFile;
//writeToFile.open("NumericalSort.txt");
//writeToFile << names[i] << '\n';
//writeToFile.close();
cout << names[i] << '\n';
}
sortFinished = true;
inFile.close();
}
sorry there's probably a lot of crap that refers to other functions I didn't include. Am I taking the wrong route?
Thank you in advance
To populate vector of strings
vector<string> names;
ifstream inFile(filename);
copy(istream_iterator<string>(inFile), istream_iterator<string>(), back_inserter(names));
To sort lexicographically
sort(names.begin(), names.end());
To sort numerically
sort(names.begin(), names.end(), cmpr());
cmpr is a custom comparator defined to compare numerical part of strings.
To get the numerical part of string, use
int num = stoi(s.substr(s.find_last_of('_') + 1));
An example in C++11
sort(names.begin(), names.end(), [](const string & a, const string & b) {
int ia = stoi(a.substr(a.find_last_of('_') + 1));
int ib = stoi(b.substr(b.find_last_of('_') + 1));
return ia < ib;
});
See http://ideone.com/1Wcnvq demo
Read the names from the file to std::vector<string>.
sort(names.begin(), names.end()).
Now,
sort(names.begin(), names.end(), comparator)
The comparator should take 2 strings as arguments and compare the number part of the strings.
Related
I want to find a specific string in a list of sentence. Each sentence is a line delimited with a \n. When the newline is reached the current search should stop and start new on the next line.
My program is:
#include <iostream>
#include <string.h>
using namespace std;
int main(){
string filename;
string list = "hello.txt\n abc.txt\n check.txt\n"
cin >> filename;
// suppose i run programs 2 times and at 1st time i enter abc.txt
// and at 2nd time i enter abc
if(list.find(filename) != std::string::npos){
//I want this condition to be true only when user enters complete
// file name. This condition also becoming true even for 'abc' or 'ab' or even for 'a' also
cout << file<< "exist in list";
}
else cout<< "file does not exist in list"
return 0;
}
Is there any way around. i want to find only filenames in the list
list.find will only find substring in the string list, but if you want to compare the whole string till you find the \n, you can tokenize the list and put in some vector.
For that, you can put the string list in std::istringstream and make a std::vector<std::string> out of it by using std::getline like:
std::istringstream ss(list);
std::vector<std::string> tokens;
std::string temp;
while (std::getline(ss, temp)){
tokens.emplace_back(temp);
}
If there are leading or trailing spaces in the tokens, you can trim the tokens before adding them to the vector. For trimming, see What's the best way to trim std::string?, find a trimming solution from there that suits you.
And after that, you can use find from <algorithm> to check for complete string in that vector.
if (std::find(tokens.begin(), tokens.end(), filename) != tokens.end())
std::cout << "found" << std::endl;
First of all I wouldn't keep the list of files in a single string, but I would use any sort of list or vector.
Then if keeping the list in a string is a necessity of yours (for some kind of reason in your application logic) I would separate the string in a vector, then cycle through the elements of the vector checking if the element is exactly the one searched.
To split the elements I would do:
std::vector<std::string> split_string(const std::string& str,
const std::string& delimiter)
{
std::vector<std::string> strings;
std::string::size_type pos = 0;
std::string::size_type prev = 0;
while ((pos = str.find(delimiter, prev)) != std::string::npos)
{
strings.push_back(str.substr(prev, pos - prev));
prev = pos + 1;
}
// To get the last substring (or only, if delimiter is not found)
strings.push_back(str.substr(prev));
return strings;
}
You can see an example of the function working here
Then just use the function and change your code to:
#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
int main(){
string filename;
string list = "hello.txt\n abc.txt\n check.txt\n"
cin >> filename;
vector<string> fileList = split_string(list, "\n");
bool found = false;
for(int i = 0; i<fileList.size(); i++){
if(fileList.at(i) == file){
found = true;
}
}
if(found){
cout << file << "exist in list";
} else {
cout << "file does not exist in list";
}
return 0;
}
Obviously you need to declare and implement the function split_string somewhere in your code. Possibly before main declaration.
So, I have a file that contains a pattern of a string then an int alternating line by line.
Something like this:
John McClane
30
James Bond
150
Indiana Jones
50
In this example, I would set John McClane to a string variable and then 30 to an integer variable. My issue is dealing with two types. I want to use getline(), but that only works with strings.
Is there an efficient or "right" way of doing this?
There are a number of approaches you could try.
Get string input, and convert to an integer if valid
Convert every second string to an integer
Try to read an integer when you expect one (just using cin >> in;). If you want a robust program, you can check validity with cin.good()
I don't know if there is a "right" way of doing this per say, but it's not a very taxing operation, so whatever you choose should be fine.
You could make a variable like this
string ibuf;
Then convert it to an integer doing this
getline(cin, ibuf);
(Whatever your int variable is) = strtol(ibuf.c_str(), NULL, 10);
One thing about C++ is that there are a large number of ways to accomplish any one task. One way to get integers from strings is to use a stringstream. There is a tutorial on stringstreams here
As for your problem with reading the alternating file, consider the following pseudocode:
boolean isInt = false;
while(fileIsNotOver) {
//getline
if(isInt) {
//use stringstream to get int here
} else {
//do whatever with the name here
}
isInt = !isInt;
}
I don't know if this fully works as i didn't tested it however it just compiles fine and answer should be something like this i think.
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
int main()
{
int counter = 0;
int number;
string test_string;
ifstream myfile ("example.txt");
if (myfile.is_open())
{
while ( getline (myfile,test_string) )
{
cout << test_string << '\n';
++counter;
if(counter % 2 == 0 ){
number = atoi(test_string.c_str());
cout << number << '\n';
}else{
cout << test_string << '\n';
}
}
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
You can try like this to read a string then an int alternating line by line.
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
int main()
{
string name;
int number;
freopen("input.txt", "r", stdin);
while (getline(cin, name))
{
cin >> number;
/*
process the input here
...
...
*/
getline(cin, name); // just to read the new line and/or spaces after the integer
//getchar(); //you can use getchar() instead of getline(cin, name) if there is no spaces after the integer
}
return 0;
}
Thanks !!!
In Lua, I have such a function to read a file into an array:
function readFile(file)
local output = {}
local f = io.open(file)
for each in f:lines() do
output[#output+1] = each
end
f:close()
return output
end
Now in C++, I tried to write that like this:
string * readFile(file) {
string line;
static string output[] = {};
ifstream stream(file);
while(getline(stream, line)) {
output[sizeof(output)+1] = line;
}
stream.close();
return output;
}
I know you can't return arrays from functions, only pointers. So I did this:
string *lines = readFile("stuff.txt");
And it threw me the error cannot convert 'std::string {aka std::basic_string<char>} to' std::string* {aka std::basic_string<char>*}' in intialization string *lines = readFile("stuff.txt");
Can anyone tell me what is wrong here, and is there a better way to read files into arrays?
EDIT:
I'm going to be using the returned array to do value matching using a for loop. In Lua this would be written as:
for _, each in ipairs(output) do
if each == (some condition here) then
--Do Something
end
end
How can this be done in C++, using vectors (according to the answer by Jerry Coffin)?
EDIT 2:
I can't match the vectors correctly for some reason. I wrote the code in a separate test file.
int main() {
vector<string> stuff = read_pass();
cout << stuff.size() << endl;
cout << stuff[0] << endl;
if (stuff[0] == "admin") {
cout << "true";
}
else {
cout << "false";
}
return 0;
}
read_pass() looks like this:
vector<string> read_pass() {
ifstream stream("stuff.txt");
string line;
vector<string> lines;
while(getline(stream, line)) {
lines.push_back(line);
}
stream.close();
return lines;
}
And stuff.txt looks like this:
admin
why?
ksfndj
I just put it some random lines to test the code. Every time I compile and run main.cpp the output I get is
3
admin
false
So why isn't the code being matched properly?
EDIT 3:
So instead of forcing myself down the vectors method of doing things, I decided to try this instead:
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <vector>
#include "basefunc.h"
using namespace std;
int main() {
string storedUsrnm;
string storedPw;
string pw = "admin";
string usrnm = "admin";
ifstream usernames("usrnm.accts");
ifstream passwords("usrpw.accts");
while(getline(usernames, storedUsrnm)) {
getline(passwords, storedPw);
print("StoredUsrnm " + storedUsrnm);
print("StoredPw: " + storedPw);
if (storedUsrnm == usrnm && storedPw == pw) {
print("True!");
return 0;
}
}
print("False!");
return 0;
}
Where print() is
void print(string str) {
cout << str << endl;
}
This still prints false, at the end, and it leads me to believe that for some reason, the "admin" read by the ifstream is different from the "admin" string. Any explanations for how this is so? Or does this code not work either?
Doesn't look to me like your current code should even compile. Anyway, I'd probably do something like this:
std::vector<std::string> read_file(std::istream &infile) {
std:string line;
std::vector<std::string> lines;
while (std::getline(infile, line))
lines.push_back(line);
return lines;
}
So the basic idea here is to read a line from the file, and if that succeeded, add that line (with push_back) to the vector of results. Repeat until reading a line from the file fails. Then return the vector of all the lines to the caller.
A few notes: especially at first, it's fairly safe to presume that any use of pointers is probably a mistake. That shouldn't be taken as an indication that pointers are terribly difficult to work with, or anything like that--just that they're almost never necessary for the kinds of things most relative beginners do in C++.
Likewise with arrays--at first, assume that what you might think of as an array in some other language translates to a std::vector in C++. C++ does also have arrays, but using them can wait a while (a long while, IMO--I've been writing C++ for decades now, and virtually never use raw pointers or arrays at all).
In the interest of simplicity, I've consolidated the data into the program, so it reads the data from the stringstream, like this:
#include <vector>
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
using namespace std;
vector<string> read_pass(istream &is) {
string line;
vector<string> lines;
while (getline(is, line)) {
lines.push_back(line);
}
return lines;
}
int main() {
istringstream input{ "admin\nwhy?\nksfndj" };
// To read from an external file, change the preceding line to:
// ifstream input{ "stuff.txt" };
vector<string> stuff = read_pass(input);
cout << stuff.size() << endl;
cout << stuff[0] << endl;
if (stuff[0] == "admin") {
cout << "true";
}
else {
cout << "false";
}
return 0;
}
At least for me, this produces:
3
admin
true
...indicating that it has worked as expected. I get the same with an external file. If you're not getting the same with an external file, my immediate guess would be that (at least the first line of) the file contains some data you're not expecting. If the problem continues, you might consider writing out the individual characters of the strings you read in numeric format, to give a more explicit idea of what you're really reading.
After a long time, I finally came up with the answer
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <map>
using namespace std;
typedef map<int, string> strArr;
strArr readFile(string file) {
ifstream stream(file);
string line;
strArr output;
while(getline(stream, line)) {
output[output.size()+1] = line;
}
stream.close();
return output;
}
It doesn't read the file into an array, but it does return a map that does basically the same thing
I need to write just a function that counts the number of integers in an already opened and good text file.
a. Assume there is a text file with a large number of integers divided by spaces
b. Write a function called analyzeFile that accepts a previously opened ifstream
file object as a parameter, and counts the number of integers in the file.
c. It does not need to do anything with the integers, but it must count exactly the
correct number of integers in the file and return that number to the calling
function.
d. It also does not need to manipulate the file operations themselves, so it does not
need to close the file or conduct any other actions other than counting the integers
and returning the number of them.
Thank you for any help on my problem!
Edit:
Here is what I have as a function do far, is it right, I don't know:
int analizeFile (ifstream &inf, const string &fileName) {
int count = 1;
int num;
fin.open(fileName.c_str() );
fin >> num;
while (fin.good() ) {
fin>> num;
count ++;
}
return count;
}
Comments:
int analizeFile (ifstream &inf, const string &fileName) {
Since the count is always a non-negative quantity, I'd prefer to use size_t rather than int. Nit: You may want to change the name of the function to analyzeFile.
int count = 1;
Problem starts here: If your file does not have any integer then you return a wrong result.
int num;
fin.open(fileName.c_str() );
No need to call open. This would typically be called by the ifstream ctor.
fin >> num;
while (fin.good() ) {
Again, this is not required. You can extract from the stream and test in the while condition -- something which is more frequently used.
fin>> num;
count ++;
}
return count;
}
You can use a functional approach too
// it was previously opened, so you don't need a filename.
int analyzeFile (istream &inf) {
std::istream_iterator<int> b(inf), e;
return std::distance(b, e);
}
If the iterator cannot read an integer, it will set the fail state on the stream and will compare equal to the end iterator. distance then returns the number of iteration steps it took to reach the end iterator.
Many many years later, you could come up with a more modern solution.
You can simply use associative containers like std__map or std::unordered:map for counting. This is more ore less the standard approach.
Then there are many many new and powerful functions availbale.
Using those, you could come up with some like:
#include <iostream>
#include <fstream>
#include <string>
#include <iterator>
#include <map>
#include <cctype>
using Counter = std::map<char, std::size_t>;
const std::string fileName{"test.txt"};
int main() {
// Open file and check, if it could be opened
if (std::ifstream ifs{fileName}; ifs) {
// Read all data
std::string text{std::istream_iterator<char>(ifs),{}};
// Define the counters
Counter upperCaseLettterCount{},lowerCaseLettterCount{};
// Iterate over all characters in the string and count
for (const char c : text) {
if (std::isupper(c)) upperCaseLettterCount[c]++;
if (std::islower(c)) lowerCaseLettterCount[c]++;
}
// Show result
std::cout << "\nUppercase count:\n\n";
for (const auto& [letter,count] : upperCaseLettterCount) std::cout << letter << " -> " << count << '\n';
std::cout << "\nLowercase count:\n\n";
for (const auto& [letter,count] : lowerCaseLettterCount) std::cout << letter << " -> " << count << '\n';
}
else
// Error, file could not be opened
std::cerr << "\n\n*** Error: Text file '" << fileName << "' could not be opened\n\n";
}
I would like to read a text file and input its contents into an array. Then I would like to show the contents of the array in the command line.
My idea is to open the file using:
inFile.open("pigData.txt")
And then to get the contents of the file using:
inFile >> myarray [size]
And then show the contents using a for loop.
My problem is that the file I am trying to read contain words and I don't know how to get a whole word as an element in the array. Also, let's say that the words are divided by spaces, thus:
hello goodbye
Could be found on the file. I would like to read the whole line "hello goodbye" into an element of a parallel array. How can I do that?
Should be pretty straightforward.
std::vector<std::string> file_contents;
std::string line;
while ( std::getline(inFile,line) )
file_contents.push_back(line);
std::vector<std::string>::iterator it = file_contents.begin();
for(; it!=file_contents.end() ; ++it)
std::cout << *it << "\n";
Edit:
Your comment about having "hello goodbye" as element zero and element one is slightly confusing to me. The above code snip will read each line of the file and store that as an individual entry in the array 'file_contents'. If you want to read it and split it on spaces that is slightly different.
For context, you could have provided a link to your previous question, about storing two lists of words in different languages. There I provided an example of reading the contents of a text file into an array:
const int MaxWords = 100;
std::string piglatin[MaxWords];
int numWords = 0;
std::ifstream input("piglatin.txt");
std::string line;
while (std::getline(input, line) && numWords < MaxWords) {
piglatin[numWords] = line;
++numWords;
}
if (numWords == MaxWords) {
std::cerr << "Too many words" << std::endl;
}
You can't have one parallel array. For something to be parallel, there must be at least two. For parallel arrays of words, you could use a declarations like this:
std::string piglatin[MaxWords];
std::string english[MaxWords];
Then you have two options for filling the arrays from the file:
Read an entire line, and the split the line into two words based on where the first space is:
while (std::getline(input, line) && numWords < MaxWords) {
std::string::size_type space = line.find(' ');
if (space == std::string::npos)
std::cerr << "Only one word" << std::endl;
piglatin[numWords] = line.substr(0, space);
english[numWords] = line.substr(space + 1);
++numWords;
}
Read one word at a time, and assume that each line has exactly two words on it. The >> operator will read a word at a time automatically. (If each line doesn't have exactly two words, then you'll have problems. Try it out to see how things go wrong. Really. Getting experience with a bug when you know what the cause is will help you in the future when you don't know what the cause is.)
while (input && numWords < MaxWords) {
input >> piglatin[numWords];
input >> english[numWords];
++numWords;
}
Now, if you really one one array with two elements, then you need to define another data structure because an array can only have one "thing" in each element. Define something that can hold two strings at once:
struct word_pair {
std::string piglatin;
std::string english;
};
Then you'll have just one array:
word_pair words[MaxWords];
You can fill it like this:
while (std::getline(input, line) && numWords < MaxWords) {
std::string::size_type space = line.find(' ');
if (space == std::string::npos)
std::cerr << "Only one word" << std::endl;
words[numWords].piglatin = line.substr(0, space);
words[numWords].english = line.substr(space + 1);
++numWords;
}
Notice how the code indexes into the words array to find the next word_pair object, and then it uses the . operator to get to the piglatin or english field as necessary.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
using namespace std;
int main()
{
// This will store each word (separated by a space)
vector<string> words;
// Temporary variable
string buff;
// Reads the data
fstream inFile("words.txt");
while(!inFile.eof())
{
inFile>>buff;
words.push_back(buff);
}
inFile.close();
// Display
for(size_t i=0;i<words.size();++i) cout<<words[i]<<" ";
return 0;
}
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main ()
{
vector<string> fileLines;
string line;
ifstream inFile("pigData.txt");
if ( inFile.is_open() ) {
while ( !inFile.eof() ) {
getline(inFile, line);
fileLines.push_back(line);
}
inFile.close();
} else {
cerr << "Error opening file" << endl;
exit(1);
}
for (int i=0; i<fileLines.size(); ++i) {
cout << fileLines[i] << "\n";
}
cout << endl;
return 0;
}