how to read from middle of text file - c++

I am writing a program that reads from two files ("joke.text"and"punchline.txt") there is "garbage" in the punchline file and i cant figure out how to read just the line i want. Please help. Also, I am using Visual Studios
this is what I have:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
fstream jokeFile;
string data;
char input;
cout << "opening the file... \n";
jokeFile.open("joke.txt", ios::in);
if (jokeFile.is_open())
{
getline(jokeFile, data);
while (jokeFile )
{
cout << data <<endl;
getline(jokeFile, data);
}
jokeFile.close();
}
else
{
cout << "ERROR: could not open that file\n";
}
cout << "would like you see the punchline? (Y/N) \n";
cin >> input;
/* if ( input == 'N' || 'n')
{
cout << "Ok, keep guessing\n";
}*/
if (input == 'Y' || 'y')
{
jokeFile.open("punchline.txt", ios::in);
if (jokeFile.is_open())
{
//getline(jokeFile, data, '\n');
while (jokeFile)
{
cout << data;
getline(jokeFile, data, '\n');
}
jokeFile.close();
}
else
{
cout << "ERROR: could not open the file\n";
}
}
/*{
cout << "invalid response, try again\n";
}*/
system("pause");
return 0;
}
and here is the output:
opening the file...
Two men who work together in a facory were talking.
"I know how to get some time off," said one.
"How are you going to do that?" asked the other.
"Watch," he said, and climbed a ladder to the ceiling.
The foreman asked what he was doing up there,
and the man replied. "I'm a lightbulb."
"I think you need some time off," the foreman
said, and the first man walked out of the
factory. After a moment, the second man followed
him. "Where do you think you're going?"
the foreman shouted.
would like you see the punchline? (Y/N)
y
asdasdasdasdasdfdssdfdsaasdfdssfddsfdsasdsad"I can't work in the dark, " he said.

There are two possibilities:
You know the position you wish to seek to. If so, then see the seek functions here: http://www.cplusplus.com/reference/istream/istream/seekg/
You do not know the position you wish to seek to, but you know what the garbage "looks like", in which case you'll need to read it and parse it till you've read past it.

You can use fseek in order to get the size of the file, than divide it to half, and start reading from half with fseek again.
int fseek(FILE *stream, long int offset, int whence)
Example: (Have not been tested)
int size = fseek(jokeFile, 0, SEEK_END);
fseek(jokeFile, size/2.0, SEEK_SET);

I think your question is misleading for the problem you're presenting. It looks like your program is trying to output all of the jokefile.txt, prompt the user, then output all of the punchline.txt file. The garbage output before the punch line must be the gibberish returned to the 'data' string when the EOF is reached on jokefile.txt. And your first read of punchline.txt is commented out, so the cout in that loop dumps the garbage before reading and outputing the punch line.

Related

Editing a text file if searched value is present

I have just a couple issues here with my code. It works but I'm not advanced enough to do what I want to yet. Nor do I know how to word it for a google search. I have a Blackjack game that I'm doing and want to know how to edit certain lines of the file depending on user input. Simpler, I want a user to be able to open the game and start with their balance from the last time they were playing(a save and load feature). My issues are the balance and the username are on the same line in the text file (purposefully) and I want to assign the variables to those in the text file. I know I'm on the right track, I just dont know where to go from here. Thanks for the help in advance. If I broke a rule of posting, I'm sorry.
input username
if username is present in file
edit balance on leave
if username isnt present in file
create new user
Here is my code for the load function:
void load(userAcc user1)
{
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
string word;
for (int x = 0; in >> word; x++);
{
user1.name = word;
user1.balance = word;
}
cout << user1.name << endl;
cout << user1.balance << endl;
in.close();
}
else
cout << "Cannot open a file";
}
void save(userAcc user1)
{
user1.balance = "1000";
cout << "Enter a username: ";
cin >> user1.name;
ofstream out;
out.open("Balances.txt", ios_base::app);
if (out.is_open())
{
out << user1.name << " " << user1.balance << endl;
out.close();
}
else
cout << "Cannot open a file";
}
In
for (int x = 0; in >> word; x++);
remove the trailing ;. It ends the statement before the body of the for loop, separating the two. The for spins around doing nothing but reading the file until it ends and incrementing the unused variable x and the following code block will be run exactly once, storing whatever is in word (and since the loop will exit when the read into word fails, what's in word will depend on the C++ Standard version the the compiler's been set to use) into user1.
Once the ; is removed, the for loop will read into word until no more words can be read from the file. Every word read is copied into the same userAcc writing over the previous word. When the file hits the end in >> word will fail and the loop will exit. The last word in the file will then be printed out, all other words having been overwritten.
Naïve fixing of this would look something like
void load(userAcc user1)
{
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
while (in >> user1.name // read name in from file
>> user1.balance) // read balance in from file
{ // loop will exit when it cannot read a name and a balance from the file
// for now we're just printing out what's read from the file.
cout << user1.name << endl << user1.balance << endl;
}
// in.close(); not needed. File will automatically close when in goes out of scope.
}
else
cout << "Cannot open a file";
}
But we probably want to do more than print out all of the users in the file, so let's put them into a convenient resizable container like std::vector.
vector<userAcc> load() // takes no parameters, returns list of accounts
{
vector<userAcc> accounts;
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
userAcc user1; // account we can read into
while (in >> user1.name >> user1.balance)
{
accounts.push_back(user1); // store account
}
}
else
cout << "Cannot open a file";
return accounts; // hand accounts read back to caller.
}
Use of the function would be something like
vector<userAcc> accounts = load();
The save function looks pretty much good-to-go as written.

Multiset: Problem with multiset adding more than one version of a word and cannot handle large amounts of text

Update and fixed: I have fixed the problem causing the error message- Huge thanks to user PaulMcKenzie for helping me understand what the error message was telling me!- When my program encountered a letter with a mark above it (diacritical marks I think they are called), it crashed. I have adjusted my code to account for these and now it doesn't crash at all! Another huge thanks to user ihavenoidea for helping me understand multisets! My program is now working the way it's supposed to!
Original post:
****I am VERY new to C++ so any and all help is appreciated!****
Ok, so I'm trying to use multiset to sort words so I can see how many times a word appears in a text. First, my program accepts a file, then it reads the words and takes out any punctuation, then it puts it into a multiset. After this, it is supposed to put the results into a text file the user names themselves.
My first issue is that the multiset seems to be creating more than one element for the same word (For example: in one of my tests I saw a(4) listed in the text document 3 times in a row instead of one time).
My Second issue is that when I try to read in large text documents (I'm using John Colliers story "Bottle Party" http://ciscohouston.com/docs/docs/greats/bottle_party.html to test it) my program completely crashes but doesn't crash when I test it with a smaller text document (small being with say about 5-10 lines of text). I'm using Visual Studios and (once again I'm new to Visual Studios also) I don't know what the error message is trying to tell me but it says:
After selecting retry:
As always, any and all help is greatly appreciated.
Code here:
#include <iostream>
#include <string> //for strings
#include <fstream> //for files
#include <set> //for use of multiset
using namespace std;
string cleanUpPunc(string);
//Global variables
multiset <string> words; //will change back to local variable later
int main() {
//Starting variables
string fileName1 = "", fileName2 = "", input = "", input2 = ""; //To hold the input file and the file we wish to print data to if desired
ifstream fileStream; //gets infor from file
//Program start
cout << "Welcome to Bags Program by Rachel Woods!" << endl;
cout << "Please enter the name of the file you wish to input data from: ";
getline(cin, fileName1);
//Trys to open file
try {
fileStream.open(fileName1);
if (!fileStream) {
cerr << "Unable to open file, please check file name and try again." << endl;
system("PAUSE");
exit(1);
}
while (fileStream >> input) {
input2 = cleanUpPunc(input); //sends the input word to check for punctation
words.insert(input2); //puts the 'cleaned up' word into the multiset for counting
}
fileStream.close();
//Sends it to a text document
cout << "Please name the file you would like to put the results into: ";
getline(cin, fileName2);
ofstream toFile; //writes info to a file
//Code to put info into text file
toFile.open(fileName2);
if (toFile.is_open()) {
multiset<string>::iterator pos;
for (pos = words.begin(); pos != words.end(); pos++) {
toFile << *pos << " " << words.count(*pos) << endl;
}
toFile.close();
cout << "Results written to file!" << endl;
}
else {
cout << "Could not create file, please try again." << endl;
}
}catch (exception e) {
cout << "Stop that. ";
cout << e.what();
}
cout << "Thanks for using this program!" << endl;
system("PAUSE");
return 0;
}
string cleanUpPunc(string maybe) {
//Takes out puncuation from string
//Variables
string takeOut = maybe;
//Method
for (int i = 0, len = maybe.size(); i < len; i++) {
if (ispunct(takeOut[i])) {
takeOut.erase(i--, 1);
len = takeOut.size();
}
}
return takeOut;
}

Why does the program end when I enter a number?

I began programming on C++ some days ago and something is really getting me troubles:
whenever I enter the number, the program ends.
Code:
using namespace std;
int main()
{
int entry;
cout << "Write a number: ";
cin >> entry;
cout << entry;
cin.get();
return 0;
}
I need some help here so my programs could run right.
By right I mean to output the number after ending... but it just ends after I enter the number and press enter It does not print it.
UPDATE:
For the sake of the ones who didn't understood what I was meaning (sorry for my english)
Ok let me explain.
-So the program is suposed to get the values from the keyboard right.
-I enter a number let´s say is 6, ok now I press enter.
-Alright now the number is supposed to be output on the screen, but this doesn´t happen because the program closes too fast.
But this was solved actually, by adding a second cin.get(); or by adding a cin.ignore(); after each data input petition.
Here's a slightly improved version that might be closer to what you wanted:
#include <string>
#include <sstream>
#include <iostream>
int main()
{
int n;
std::string line;
while (true)
{
std::cout << "Please enter an integer: ";
if (!(std::getline(std::cin, line))) { return 1; /* error! */ }
std::istringstream iss(line);
if (iss >> n) { break; }
}
std::cout << "Thank you. You said: " << n
<< "\n\nPlease press Enter to quit.";
std::getline(std::cin, line);
}
The error condition in the getline is triggered when the input stream is closed or otherwise terminated before another line could be read (e.g. if you hit Ctrl-D on the console). The token extraction into n fails until you enter a valid integer, and the loop will continue looping until this happens.
All you need to do is consume the newline that is left over after reading the integer.
This happens in java as well.
using namespace std;
int main()
{
int entry;
cout << "Write a number: ";
cin >> entry;
cout << entry;
cin.get(); //Consume newline
cin.get();
return 0;
}
get() reads one and only one character from the stream, so it's perfectly normal that the program ends after you enter your number.
Have a look either at the std::basic_istream<>::getline() method or, easier, std::getline() which doesn't require a dynamic buffer.
If you need more information about basic IO in C++, you can read the following documentation : Basic Input/Output - C++
Update
Like stated in the comments, I missunderstood the question, I was initially thinking that only one digit was read into the variable.
After reading carefully again, I'm unable to understand what the problem is.
The reason for this is youre using
cin.get();
return 0;
at the end of the program , the cin.get() reads the number you entered then goes straight to return 0; thus ending the program.
to stop this you can add an extra cin.get(); before return 0;
or use
system("Pause");
before return 0; instead

Why is this word sorting program only looping once?

I'm trying to create a word sorting program that will read the words in a .txt file and then write them to a new file in order from shortest words to longest words. So, for instance, if the first file contains:
elephant
dog
mouse
Once the program has executed, I want the second file (which is initially blank) to contain:
dog
mouse
elephant
Here's the code:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string word;
ifstream readFrom;
ofstream writeTo;
readFrom.open("C:\\Users\\owner\\Desktop\\wordlist.txt");
writeTo.open("C:\\Users\\owner\\Desktop\\newwordlist.txt");
if (readFrom && writeTo)
{
cout << "Both files opened successfully.";
for (int lettercount = 1; lettercount < 20; lettercount++)
{
while (readFrom >> word)
{
if (word.length() == lettercount)
{
cout << "Writing " << word << " to file\n";
writeTo << word << endl;
}
}
readFrom.seekg(0, ios::beg); //resets read pos to beginning of file
}
}
else
cout << "Could not open one or both of files.";
return 0;
}
For the first iteration of the for loop, the nested while loop seems to work just fine, writing the correct values to the second file. However, something goes wrong in all the next iterations of the for loop, because no further words are written to the file. Why is that?
Thank you so much.
while (readFrom >> word)
{
}
readFrom.seekg(0, ios::beg); //resets read pos to begin
The while loop will continue until special flags are set on readFrom, namely, the EOF flag. Seeking to the beginning does not clear any flags, including EOF. Add the following line right before the seek to clear the flags and your code should work fine.
readFrom.clear();
After seek, clear the EOF flag.
readFrom.clear();

Reading file input as opcode in C++

I am working on a project for a class at school. It is a simple implementation of stacks and queues. However as part of the project we are require to read opcode in from a file. The opcode is formated as follows:
append 10
serve
append 20
append 30
serve
push 10
push 50
push 20
push 20
pop
My problem is when I read in the file through a standard fstream it seems to pick up some kind of weird formatting or something, and won't match comparison checks.
I am wonder what I am doing wrong, how to fix it, and if there is a better way to manipulate opcode going forward. As it is, the if-else statement always goes to if. Kind of desperately need to get this working.
#include "StackAndQueue.h"
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
int main(){
Stack leStack;
Queue leQueue;
//Read in the datafile.
cout << "Reading default file: p2datafile.txt";
fstream data("p2datafile.txt");
while (data.fail()){
cout << " failed." << endl;
data.close();
cout << "Please enter path to datafile: ";
string filename;
cin >> filename;
data.open(filename.c_str());
}
cout << endl << "Sucess!" << endl;
//Loop through all the commands in the file
while(!data.eof()){
// Determine what kind of command is running
// and if parsing will be needed.
string opcode;
getline(data,opcode,' ');
if (opcode == "pop"){
cout << "popping!" << endl;
leStack.pop();
}
else if (opcode == "serve"){
cout << "serving" << endl;
leQueue.serve();
}
else if (opcode == "push"){
cout << "pushing";
}
else{
cout << "else!" << endl;
}
}
data.close();
system("pause");
return 0;
}
I apologize if the code is difficult to read, and the general half-finished nature of it. I am still pretty new to this.
getline used in that way considers just ' ' as a delimiter, so it won't stop at newlines; moreover, you're not extracting the argument (when the opcodes has any), so it will get read as an opcode (sticked in front of the real opcode) at the next iteration.
In my opinion, you could simply get away with using just the normal operator>>. It stops correctly at any whitespace (which is what you want to do) and supports the C++ strings correctly. The important thing is to remember to extract also the argument when needed (again, with operator>>), watching for istream::fail() errors in case of bad number formatting. You may even want to have the stream rise exceptions in case of these errors (so they don't go unnoticed).
try
{
string opcode;
data.exceptions(ios::failbit);
//Loop through all the commands in the file
while(data>>opcode){
// Determine what kind of command is running
// and if parsing will be needed.
int argument;
if (opcode == "pop"){
cout << "popping!" << endl;
leStack.pop();
}
else if (opcode == "serve"){
cout << "serving" << endl;
leQueue.serve();
}
else if (opcode == "push"){
cout << "pushing";
data >> argument;
}
else if (opcode == "append"){
cout << "appending";
data >> argument;
}
else{
cout << "else!" << endl;
}
}
data.close();
}
catch(const ios::failure & ex)
{
if(!data.eof())
cout<<"IO error"<<endl;
}
the problem you are most likely having stems from the way you are reading input. std::getline(..., ' ') extracts a string that ends with a space. With the given input, the first string gotten will be append, but the second will be
10
serve
append
because there are no spaces.
How about this, instead of trying to read in an opcode exactly, read a line, and see if you can figure out if it begins with an opcode.
Rather than reading the file a word a a time, read the entire line (using std::getline) then use a std::stringstream to process the line, something like this:
std::string line;
while(std::getline(file,line))
{
std::stringstream linestream(std::stringstream::in|std::stringstream::out);
linestream << line;
std::string command;
if(std::getline(linestream,command,' '))
{
//process line - chain 'if(std::getline(linestream,command,' '))' to advance the token steam
}
else
//error blank line
}