Sorting function with output to a separate file - c++

I need to sort the input text document and write it into another test document. so that lines with an even number of words delete every second word, and lines with an odd number of words remain unchanged. The sorting should be performed as a function, the first pointer to the input of the string and the second pointer to the output.
I did the input and output, tried to do a sorting to begin with without a function, but nothing worked. can you tell me what I did not do the rule?
Example:
Input.txt
I do not like to go to school or study
I like to play games
Output.txt
I not to to or
I like to play games
string in, out;
cin >> in;
cin >> out;
ifstream input(in);
string str;
ofstream output(out);
string st;
while (getline(input, str))
{
do
{
int i = count(str.cbegin(), str.cend(), ' ');
if (i % 2 == 0)
st.append(str);
else
st.append(" ");
i++;
}
while (input);
output << st << endl;
}

The std::stringstream will be your friend.
Read about it here and especially for the useful std::istringstream here.
What can we do with this thing? We can put a string into it and then use the extraction operator >> like with any other stream.
So, counting words will become very simple. We read a line from the file as a std::string and then put it into a std::istringstream, Then we extract dummy words and increase a counter until the reading fails. Then we have the number of words.
Example:
// We read one line. Count the number of words
// Put the string in a stringstream to easily extract words
std::istringstream iss(line);
// Counter for words
unsigned int wordCount{};
// Dummy word
std::string tempWord{};
// Do the counting
while (iss >> tempWord) ++wordCount;
The stream tries too read until it cannot read any more (the stream is empty) and then sets a failbit. The failure of any stream can be checked with its bool operator or the !-operator. The while loop expects a boolean. And the extraction operation iss >> tempWord will return a reference to the stream and then its bool operator will be called and used as condition.
OK, understood, we can now count words. Next step is to skip words.
This is similar simple. In case of even number of words in a string, we will read always 2 words in a loop and simply throw the second one away.
Like this
std::string usedWord{};
// Read 2 words, ignore the second
while (iss >> usedWord >> tempWord)
result += (usedWord + ' ');
There are of course many other possible ways to solve that, but maybe the below solution can give you an idea.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
int main() {
// First read the input filename
std::cout << "\nPlease enter the path for the input file:\n";
std::string inputFileName{};
std::getline(std::cin, inputFileName);
// next read the output filename
std::cout << "\nPlease enter the path for the output file:\n";
std::string outputFileName{};
std::getline(std::cin, outputFileName);
// Open now both files. If one file cannot be opened, then no need tocontinue
// Open input file and check, if it could be opened
std::ifstream inputFileStream(inputFileName);
if (inputFileStream) {
// Input file could be opened without error.
// Now open output file and check, if it could be opened
std::ofstream outputFileStream(outputFileName);
if (outputFileStream) {
// Now, both input and output file streams are open
// Read lines
std::string line{};
while (std::getline(inputFileStream, line)) {
// We read one line. Count the number of words
// Put the string in a stringstream to easily extract words
std::istringstream iss(line);
// Counter for words
unsigned int wordCount{};
// Dummy word
std::string tempWord{};
// Do the counting
while (iss >> tempWord) ++wordCount;
// if the number of words is even then
if ((wordCount % 2) == 0) {
// Rinitialize stringstream
iss.clear(), iss.str(line);
// Here we store the resulting sentence
std::string result{};
// Now extract the word that we will keep and the other one that we will throw away
std::string usedWord{};
// Read 2 words, ignore the second
while (iss >> usedWord >> tempWord)
result += (usedWord + ' ');
// Write result to output
outputFileStream << result << '\n';
}
else
outputFileStream << line << '\n';
}
}
else std::cerr << "\n*** Error: could not open output file '" << outputFileName << "'\n\n";
}
else std::cerr << "\n*** Error: could not open input file '" << inputFileName << "'\n\n";
}

Related

File Line count using boost

If I have a boost::filesystem::path object, how can I get the line count of this file?
I need to compare the line counts of two files as a precondition check.
You can do something like this:
std::ifstream file(path.c_str());
// Number of lines in the file
int n = std::count(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>(), '\n');
Where path is a boost::filesystem::path. This will count the number of \n in the file so you need to pay attention if there is a \n at the end of the file to get the right number of lines.
You can use ifstream and getline to read file by line, and count it.
std::ifstream filein("aaa.txt");
int count = 0;
std::string line;
while (std::getline(filein, line))
{
count++;
}
std::cout << "file line count is " << count;
With stringstream, I advise to use intermediate string otherwise the streamstring will be consumed during the count and the iterator will not be at the beginning of the string for next getline.
string s = string("1\n2\n3\nlast");
stringstream sstream(s);
int nbOfLines = std::count(s.begin(), s.end(), '\n');
cout << "Nb of lines is: " << nbOfLines << endl;
Result:
Nb of lines is: 3
You can do getline from the beginning.
Or, for better performances (less copy), seek back to the beginning
int nbOfLines = std::count(std::istreambuf_iterator<char>sstream),std::istreambuf_iterator<char>(), '\n');
sstream.seekg(0, ios_base::beg);

C++ file reading

I have a file that has a number in which is the number of names that follow. For example:
4
bob
jim
bar
ted
im trying to write a program to read these names.
void process_file(ifstream& in, ofstream& out)
{
string i,o;
int tmp1,sp;
char tmp2;
prompt_user(i,o);
in.open (i.c_str());
if (in.fail())
{
cout << "Error opening " << i << endl;
exit(1);
}
out.open(o.c_str());
in >> tmp1;
sp=tmp1;
do
{
in.get(tmp2);
} while (tmp2 != '\n');
in.close();
out.close();
cout<< sp;
}
So far I am able to read the first line and assign int to sp
I need sp to be a counter for how many names. How do I get this to read the names.
The only problem I have left is how to get the names while ignoring the first number.
Until then i cannot implement my loop.
while (in >> tmp1)
sp=tmp1;
This successfuly reads the first int from the and then tries to continue. Since the second line is not an int, extraction fails, so it stops looping. So far so good.
However, the stream is now in fail state, and all subsequent extractions will fail unless you clear the error flags.
Say in.clear() right after the first while loop.
I don't really see why you wrote a loop to extract a single integer, though. You could just write
if (!(in >> sp)) { /* error, no int */ }
To read the names, read in strings. A loop is fine this time:
std::vector<std::string> names;
std::string temp;
while (in >> temp) names.push_back(temp);
You'd might want to add a counter somewhere to make sure that the number of names matches the number you've read from the file.
int lines;
string line;
inputfile.open("names.txt");
lines << inputfile;
for(i=0; i< lines; ++i){
if (std::getline(inputfile, line) != 0){
cout << line << std::endl;
}
}
First of all, assuming that the first loop:
while (in >> tmp1)
sp=tmp1;
Is meant to read the number in the beginning, this code should do:
in >> tmp1;
According to manual operator>>:
The istream object (*this).
The extracted value or sequence is not returned, but directly stored
in the variable passed as argument.
So don't use it in condition, rather use:
in >> tmp1;
if( tmp1 < 1){
exit(5);
}
Second, NEVER rely on assumption that the file is correctly formatted:
do {
in.get(tmp2);
cout << tmp2 << endl;
} while ( (tmp2 != '\n') && !in.eof());
Although whole algorithm seems a bit clumsy to me, this should prevent infinite loop.
Here's a simple example of how to read a specified number of words from a text file in the way you want.
#include <string>
#include <iostream>
#include <fstream>
void process_file() {
// Get file name.
std::string fileName;
std::cin >> fileName;
// Open file for read access.
std::ifstream input(fileName);
// Check if file exists.
if (!input) {
return EXIT_FAILURE;
}
// Get number of names.
int count = 0;
input >> count;
// Get names and print to cout.
std::string token;
for (int i = 0; i < count; ++i) {
input >> token;
std::cout << token;
}
}

How to check if stringstream>>string will put nothing on the string?

For example, when parsing a text file, some times this file have stuff like this:
keyword a string here
keyword another string
keyword
keyword again a string
Note that the 3th line have an empty string (nothing or white spaces).. The thing is that when you do stringstream>>laststring, and stringstream have an empty string (null or just white space), it will not overwrite the "laststring", it will do nothing. Theres anyway to check this situation before hand? I dont want to create a temp empty string just to check it is still empty after stringstream>>, seems lame.
When you cannot read from stream - its state changes, so when casting to bool, it returns false:
bool read = static_cast<bool>(ss >> laststring);
Or - in if-expr:
if (ss >> laststring)
cout << "Just read: " << laststring;
See example
You can only know after trying to read whether there was something or not. What you might be able to do is to skip whitespace and see if there is a non-space in the next location:
if ((in >> std::ws).peek() != std::char_traits<char>::eof()) {
...
}
Given that empty strings are cheap to create, I wouldn't bother and try read the string. Note, however, that reading from streams isn't line based, i.e., in your case above you need to split the lines first or use something like std::getline() to read the second part of line.
You can use getline, to read a line from the file. Then, copy the line into a string stream and read words from the string stream one at a time. The streams will automatically stop reading when they run out of lines / words.
// open file
std::ifstream fin("text.txt");
// 'iterate' through all the lines in the file
unsigned lineCount = 1;
std::string line;
while (std::getline(fin, line))
{
// print the line number for debugging
std::cout << "Line " << lineCount << '\n';
// copy line into another stream
std::stringstream lineStream(line);
// 'iterate' through all the words in the line
unsigned wordCount = 1;
std::string word;
while (lineStream >> word)
{
// print the words for debugging
std::cout << '\t' << wordCount++ << ' ' << word << '\n';
}
}
You need to include iostream, fstream, sstream and string.
For checking if string is empty, use foo.size() == 0.
For checking if string stream is empty fooStream.rdbuf()->in_avail() == 0

C++: Using ifstream with getline();

Check this program
ifstream filein("Hey.txt");
filein.getline(line,99);
cout<<line<<endl;
filein.getline(line,99);
cout<<line<<endl;
filein.close();
The file Hey.txt has alot of characters in it. Well over a 1000
But my question is
Why in the second time i try to print line. It doesnt get print?
The idiomatic way to read lines from a stream is this:
std::ifstream filein("Hey.txt");
for (std::string line; std::getline(filein, line); )
{
std::cout << line << std::endl;
}
Notes:
No close(). C++ takes care of resource management for you when used idiomatically.
Use the free std::getline, not the stream member function.
According to the C++ reference (here) getline sets the ios::fail when count-1 characters have been extracted. You would have to call filein.clear(); in between the getline() calls.
#include<iostream>
using namespace std;
int main()
{
ifstream in;
string lastLine1;
string lastLine2;
in.open("input.txt");
while(in.good()){
getline(in,lastLine1);
getline(in,lastLine2);
}
in.close();
if(lastLine2=="")
cout<<lastLine1<<endl;
else
cout<<lastLine2<<endl;
return 0;
}
As Kerrek SB said correctly There is 2 possibilities:
1) Second line is an empty line
2) there is no second line and all more than 1000 character is in one line, so second getline has nothing to get.
An easier way to get a line is to use the extractor operator of ifstream
string result;
//line counter
int line=1;
ifstream filein("Hey.txt");
while(filein >> result)
{
//display the line number and the result string of reading the line
cout << line << result << endl;
++line;
}
One problem here though is that it won't work when the line have a space ' ' because it is considered a field delimiter in ifstream. If you want to implement this kind of solution change your field delimiter to e.g. - / or any other field delimiter you like.
If you know how many spaces there is you can eat all the spaces by using other variables in the extractor operator of ifstream. Consider the file has contents of first name last name.
//file content is: FirstName LastName
int line=1;
ifstream filein("Hey.txt");
string firstName;
string lastName;
while(filein>>firstName>>lastName)
{
cout << line << firstName << lastName << endl;
}

Advance File Pointer to skip over number in a file

I was wondering If I could jump positions in a text file.
Suppose I have this file.
12
8764
2147483648
2
-1
Whenever I try to read the third number it won't read because its larger than the max number for a 32 bit int.So whenever i reach the third number, it keeps reading the second over and over again. How can I jump to the 4th number?
Use std::getline instead of operator>>(std::istream, int)
std::istream infile(stuff);
std::string line;
while(std::getline(infile, line)) {
int result;
result = atoi(line.c_str());
if (result)
std::cout << result;
}
The reason you are experiencing the behavior that you are, is that when the std::istream tries (and fails) to read in an integer, it sets a "badbit" flag which means that something went wrong. As long as that badbit flag remains set, it won't do anything at all. So it's not actually re-reading in that line, it's doing NOTHING, and leaving the value that had been there alone. If you want to keep more in line with what you already had, it's probably like below. The above code is simpler and less error prone though.
std::istream infile(stuff);
int result;
infile >> result; //read first line
while (infile.eof() == false) { //until end of file
if (infile.good()) { //make sure we actually read something
std::cout << result;
} else
infile.clear(); //if not, reset the flag, which should hopefully
// skip the problem. NOTE: if the number is REALLY
// big, you may read in the second half of the
// number as the next line!
infile >> result; //read next line
}
You can first read the line, then convert the line to integer if you can. Here is an example for your file :
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
int main()
{
std::ifstream in("file");
std::string line;
while (std::getline(in, line)) {
int value;
std::istringstream iss(line);
if (!(iss >> value)) {
std::istringstream iss(line);
unsigned int uvalue;
if (iss >> uvalue)
std::cout << uvalue << std::endl;
else
std::cout << "Unable to get an integer from this" << std::endl;
}
else
std::cout << value << std::endl;
}
}
As an alternative to using std::getline(), you could call std::ios::clear(). Consider this excerpt from your previous question
fin >> InputNum;
You could replace that code with this:
fin >> InputNum;
if(!fin)
fin.clear();