C++ opening a .txt file and creating a new one - c++

So I'm trying to make a program that will open up a text file based on user input (or fail trying), read the contents and create a new text file named after user input and rewrite the contents, but this time with row numbers.
I'm having trouble that I can't pin down on anything specific. My Qt Creator gives no massive errors, but the program seems to halt around line 30 without any error messages.
An online C++ compiler complained about a segmentation problem.
The code:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main() {
string input_file = "";
cout << "input file: ";
getline(cin, input_file);
vector<string> line_vector;
string output_file = "";
cout << "output file:";
getline(cin, output_file);
ifstream file_object(input_file);
if ( not file_object )
{
cout <<"Error! The file " << input_file <<
" cannot be opened." << endl;
} else {
int row_number = 0;
string line;
while (getline(file_object, line) )
{
cout << line << endl;
line_vector[row_number] = line;
++row_number;
}
file_object.close();
ofstream output_file;
output_file.open("");
for (int i = 0; i>=row_number;++i)
{
output_file << i << " " << line_vector[i] << "\n";
}
output_file.close();
}
}

What the code is doing on this line: line_vector[row_number] = line is not actually appending to the vector - which is what you are trying to do -, but instead accessing an element at the index i inside the vector, and trying to assign line to it.
This would be fine, however, since the vector has 0 size, it is trying to assign line to unknown memory. This leads to undefined behaviour, and often, crashes. You can do one of two things. The first is to use resize or reserve to reserve the memory for the vector, then assign the variable, like so:
while (getline(file_object, line) ) {
cout << line << endl;
line_vector.resize(row_number + 1);
line_vector[row_number] = line;
++row_number;
}
However, this is pretty pointless, and instead, you should use push_back:
while (getline(file_object, line) ) {
cout << line << endl;
line_vector.push_back(line);
}
nullifying the use of row_number in that loop.
Oh, and don't use using namespace std, it's a bad habit.

"When you define a vector like line_vector, it starts out empty. All indexing into it will be out of bounds and lead to undefined behavior. Please learn how to push back elements into a vector. "
– Some programmer dude

Related

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;
}

Looping over file, only get one line

Say we have a text file with this contents:
dogs
cats
bears
trees
fish
rocks
sharks
these are just words separated by newline chars. I am trying to create a Node.js addon. The Addon will read through a file and replacing matching lines with a blank line. Say I pass my program a regex that matches /trees/. If I pass the file to my C++ program it will read + write to the file, and result in:
dogs
cats
bears
fish
rocks
sharks
Right now, the problem is it's not looping through all the lines in the file. I get the feeling that's opening the file in append mode and therefore just starting at the end of the file? I can't tell. Anyway, I want to edit the file in place, not truncate and re-write or replace the whole file, because this will interrupt processes which are tailing the file.
Here's the code:
#include <nan.h>
#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
using namespace std;
void Method(const Nan::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(Nan::New("world").ToLocalChecked());
}
void Init(v8::Local<v8::Object> exports) {
fstream infile("/home/oleg/dogs.txt");
if(infile.fail()){
cerr << " infile fail" << endl;
exit(1);
}
int pos = 0;
string line;
int count = 0;
while (getline(infile, line)){
// we only seem to loop once, even though the file has 7 or 8 items
count++;
long position = infile.tellp();
cout << "tellp position is " << position << endl;
string str(line);
int len = str.length();
cout << " => line contains => " << line << endl;
cout << " line length is " << len << endl;
std::string s(len, ' '); // create blank string of certain length
infile << s; // write the string to the current position
pos = pos + len;
cout << "pos is " << pos << endl;
}
cout << " => count => " << count << endl;
infile.close();
exports->Set(Nan::New("hello").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(Method)->GetFunction());
}
NODE_MODULE(hello, Init)
to compile the code you might need to use Node.js tooling, which is
node-gyp rebuild
If you want to help and want to try to compile the code, then let me know, because you may need more info. But I am a new C++ newb and I think someone can help me figure it out without compiling/running the code. Thanks.
To answer your question on why you only read one line of the input file:
Your first write to the file likely sets the eofbit on the stream, so the second getline() attempt will think it has no more to read.
The comment from #RSahu describes the simplest way to do this for text files.

ifstream opens files named by cin but not when pulled from an array. Any difference between cin and a regular string definition?

TL;DR File names stored as strings in array (using new) - ifstream won't open them (perror returns "No such file or directory"). Swap out array variable with a call to the user to name the file (using cin) - ifstream opens the file. Why? How do I get the array to work?
Things to Know
All files exist in folders with naming scheme run20### where
All files are named S20###.ABC where ### is the same from the parent directory and ABC can go from 001-999. These are all text files (there are no .txt extensions though) that CAN be opened by ifstream and getline.
I'm writing a program that's going to pull information from up to 150 files. An early version I wrote had the user input the file name (using cin). ifstream took the stored name and opened the file successfully every time. Obviously, I don't want to type 150 file names in so the program stores all of the file names as strings in an array for the program to pull from. However, when it goes to open the file (in the correct path and with the correct file name and extension), the error I get from perror returns "No such file or directory." If I just do a quick swap of the variables though so that the file name comes from cin, the file opens. Why would cin work and the array version not? Is there any way to get the array to work?
I've also tried something similar where there is no array. Instead, in the for loop that would pull the files from the array, the file gets named each time.
Here's the code (sorry about the headers, couldn't get it to format right):
#include <iostream>
#include <array>
#include <string>
#include <ctime>
#include <cstring>
#include <fstream>
#include <sstream>
using namespace std;
int main() {
//--------------------------Initial setup----------------------------------
cout << "Please give the full name of the folder you would like to open in the /Users/lucas/HPS/TDCData directory" << endl << endl;
string sFolderName;
cin >> sFolderName;
// Create path. I have mine here but you'll have to change it to something you'll
// use if you want to run the code
string sPathName = "/Users/lucas/HPS/TDCData/" + sFolderName;
//----------------Create file name array------------------------------------
// Get naming base from the folder name given
string sFileBase = "S20";
for (int i = 5; i <= sFolderName.length(); i++){
sFileBase = sFileBase + sFolderName[i];
}
//Specify range since different directories have different numbers of files
cout << "Files must be named S20###.ABC" << endl;
cout << "Specify a range for ABC" << endl;
int iFloor;
int iCeiling;
cout << "Floor: " << endl;
cin >> iFloor;
cout << "Ceiling: " << endl;
cin >> iCeiling;
// Define an array to store names and then store them
string *aFiles;
int iFilesSize = iCeiling - iFloor + 1;
aFiles = new string [iFilesSize];
cout << "Array created" << endl;
for (int i = iFloor; i <= iCeiling; i++){
string name = sFileBase;
if (i < 10){
name = name + ".00" + to_string(i);
}
else if (i < 100) {
name = name + ".0" + to_string(i);
}
else {
name = name + '.' + to_string(i);
}
aFiles[i-1] = name;
}
//----------------Open each file in aFiles----------------------
for (int i = 0; i < iFilesSize; i++){
// There are two important lines of code here. The first gets sFileName from
// aFiles. The second gets sFileName from user input using cin (this is commented out).
// Obviously, none of the last section of code is needed for the second line to work.
// The first line does not work for me. The second does.
string sFileName;
//First
sFileName = aFiles[i];
//Second
//cin >> sFileName
string sFullPath = sPathName + "/" + sFileName;
cout << "Searching ... " << sFullPath << endl << endl;
//Open file
ifstream inputFile(sFullPath);
//Check that the file opened
if (! inputFile.is_open()) {
cout << "Error reading" << sFullPath << endl;
perror("Error is: ");
return 0;
}
else {
cout << "File opened successfully..." << aFiles[i] << endl << endl;
}
}
cout << "All files opened..." << endl << endl;
return 0;
}
Also here's a link to a zip of one of the directories for any tests someone might want to run. Thanks for any and all help!
It looks like you start filling aFiles from index iFloor, while you start reading aFiles from index 0.
How about changing aFiles[i-1] = name; to aFiles[i-iFloor] = name;
"TL;DR File names stored as strings in array (using new)"
Don't do this. Use a dynamic container like std::vector<std::string> instead.
"- ifstream won't open them (perror returns "No such file or directory")."
Use the debugger to check what's actually passed to the
ifstream inputFile(sFullPath);
with sFullPath.
"Swap out array variable with a call to the user to name the file (using cin) - ifstream opens the file. Why? How do I get the array to work?"
You cannot replace the behaviors of a stream getting values as you're trying with the array.
The best way to make the input stream source transparent, is to simply use a std::istream reference, and don't care if it's std::cin or e.g. a std::istringstream reference.
The std::string instance needed to initialize the mentioned std::istringstream can be build e.g. using a std::ostringstream and pass the str() property to the std::istringstream constructor.

Input Numbers from txt file to array

I'm pretty sure that this is a common question, but I can't find an example similar to mine, so..
I have a text file called input.txt that has: 0.0001234 1.0005434 0.0005678 1.0023423 0.00063452 1.0001546 0.00074321 1.00017654 in it.
Now I want to write a program to read that into an array, and then quickly check that it worked. So far, I've got:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main ()
{
double PA[8];
int n;
ifstream myfile ("input.txt");
if (myfile.is_open())
{
while (! myfile.eof() )
{
getline (myfile,line);
cout << PA[line]<< endl;
}
myfile.close();
}
else cout << "Unable to open file";
for (n=0; n<8; n++) // to print the array, to check my work
{
cout << " {" << PA[n] << "} ";
}
system("PAUSE");
return 0;
}
My problem so far is that I keep getting the error: line was not declared. And I want to use the array later with floats to calculate new data. I think I'm doing it wrong for that.. Any help? Thanks!
declare line variable
int n, line = 0;
std::string value;
proper load data:
getline (myfile,value);
PA[line] = atof(value.c_str());
cout << PA[line]<< endl;
line++;
the variable line here
cout << PA[line]<< endl;
is not declared. and if you declare it, you also have to give it a value, otherwise it's undefined and the PA[line] is undefined, in other words: will crash.
the entire while block seems suspicious:
while (! myfile.eof() )
{
getline (myfile,line);
cout << PA[line]<< endl;
}
are you sure about the getline call?
I know a getline with this signature:
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
and that does not even match the number of arguments in your version.
if your input file has more than 8 lines on input, than the while loop will have that many interations - and your array has only space for 8 elements.
You need to declare the variable you called 'line' as follows:
int i=0;
while (! myfile.eof() && i<8)
{
std::string line; // this was the missing statement
getline (myfile,line);
double value = atof(line.c_str()); // convert line form char* to double
PA[i++] = value;
cout << value << endl;
}
Note that you need to convert line as double and use increment variable 'i' (for example, as I did. Make sure not overflowing PA capacity by checking i agains the size (currently 8, which should not hard coded, btw).
Also note that you shouldn't print the result if file access failed.

Linux | Segmentation Fault in C++ - Due to the function ifstream

I think I should begin by saying that I've just installed linux(debian) in my pc and have zero foreknowledge about doing things in linux. This problem is probably due to some really simple thing.
The relevant part of the code is similar to this:
ifstream stockNames("names.txt");
while (!stockNames.eof())
{
string snline;
getline(stockNames,snline);
cout << snline << endl;
.
.
.
}
this should print the first line of the file 'names.txt'. Instead it prints an empty line. And when I try to use snline as an input in another function I get the error 'Segmentation Fault'. How should I modify my code to do this? Is there any difference in usage of the ifstream in linux? Cause in windows the code works just fine
I've written the simple code below
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
int main(int argc, const char *argv[])
{
string dos = "names.txt";
ifstream stockNames(dos.c_str() );
string snline;
while (getline(stockNames,snline))
{
cout << snline << " ";
}
return 0;
}
content of names.txt is
ABC
DEFG
HBO
instead of showing those line, cout << snline produces nothing
One more update: I've written two more codes.
(1)
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
int main(int argc, const char *argv[])
{
cout << "program has initiated" << endl;
ifstream stockNames("names.txt");
if( !stockNames )
cout << "unable to open" << endl;
string snline;
while (getline(stockNames,snline))
{
cout << snline << endl;
}
return 0;
}
Result is as I desired. First 'program has initiated', then ABC, DEFG, HBO in different lines. But when I change this part
cout << snline << endl;
as
cout << snline << " hey" << endl;
Then ABC DEFG HBO does not appear and instead the only output is " hey".
This is crazy, how can this be??
btw I tried to make a debug with ddd and when I check the variable snline, ddd prints the following line
(gdb) print snline
$2 = {static npos = 4294967295, _M_dataplus = {> = {<__gnu_cxx::new_allocator> = {}, }, _M_p = 0x804c1a4 "ABC\r"}}
new mini update: when I change the relevant line as "cout << snline << " " << endl;" what's printed out is ' BC' ' FGH' ' BO' in seperate lines. Why does << operator overwrites over snline??
First of all, your while loop is wrong, because the eof flag (or any other failure flag) is set after an attempt to read from the stream fails; that means, if the attempt to read fails using getline(), the loop doesn't immediately exit, instead the loop continues which is a serious bug with your code.
So write your loop as (an idiomatic way):
string snline;
while (getline(stockNames,snline))
{
cout << snline;
//..
}
std::getline returns istream&, which can be implicitly converted into boolean type. So if getline reads successfully, then the returned value converts to true and the loop will continue, or else it will convert to false and the loop will exit.
After the edit in your question, all I can say that you need to check the stream object before using it to read contents from the file. More specifically, you need to check if the stream has been initialized properly and it has indeed opened the input file (i.e names.txt), and is ready to read data from it.
So try doing this:
//...
ifstream stockNames(dos.c_str() );
if (!stockNames)
{
cout << "file couldn't open properly" << endl;
return 0;
}
string snline;
while (getline(stockNames,snline))
{
cout << snline << " ";
}
Now run this, and see what it prints.
You have a dos file which uses \r\n at the end of each line. Linux doesn't recognise \r as part of the line ending so it gets included in the snline string. \r causes the next thing printed to appear at the beginning of the line so " hey" overwrites the stock names you were expecting to see.
Try
cout << snline << " " << endl;
and you'll see what I mean
stockNames will not actually appear to reach "end of file" until it has tried to input something and received the EOF character. Therefore, you're going to want to re-write your loop as follows:
ifstream stockNames("names.txt");
string snline;
getline(stockNames,snline);
while (!stockNames.eof())
{
cout << snline << endl;
.
.
.
getline(stockNames,snline);
}
or much simply
ifstream stockNames("names.txt");
string snline;
while (getline(stockNames, snline))
{
cout << snline << endl;
.
.
.
}
To answer your question; no, there is no significant difference in how ifstream operates on Linux and Windows. Of course if you write bugged code (as the other two answers have pointed out) then you might run into problems, but I can't see how the eof bug would cause the problems you are describing.
I would concentrate on the part where you say that using snline causes a segmentation fault, that indicates a more serious issue. Could you post code that demonstrates that?
You aren't checking whether the stream opened properly.
std::ifstream stockNames("names.txt");
if (! stockNames) {
std::cerr << "Unable to open file 'names.tex'\n";
}
else {
// Do the rest of your stuff here.
}
Always check status. Don't just plow ahead and assume everything is OK.
BTW, using namespace std; is something you see in a lot of bad books that are trying to save a tiny amount of ink. Try to get out of this bad habit.