I am currently trying to learn C++. I am given a .txt file that contains a different individuals data on each line. I want to read that data into an array of strings. I don't see anything wrong with this function and I have done the same thing before, but for some reason I am receiving a segmentation fault.
#include <string>
#include <iostream>
#include <fstream>
void readFile(std::istream&, std::string*);
int lineCount(std::istream&);
int main(){
std::ifstream inFile("input.txt");
int numLines = lineCount(inFile);
std::string data[numLines];
inFile.close();
inFile.open("input.txt");
readFile(inFile, data);
inFile.close();
return 0;
}
int lineCount(std::istream& inFile){
std::string line;
int numLines = 0;
while(std::getline(inFile, line)){
numLines++;
}
return numLines;
}
void readFile(std::istream& inFile, std::string *data){
int i = 0;
while(std::getline(inFile, data[i])){
std::cout << i << "\n"; //testing values
std::cout << data[i] << "\n"; //testing values
i++;
}
}
Here is the output of the above code.
//Output
//Note, these are fictional people
0
Florence,Forrest,1843 Glenview Drive,,Corpus Christi,TX,78401,10/12/1992,5/14/2012,3.215,127/11/1234,2.5,50
1
Casey,Roberta,3668 Thunder Road,,Palo Alto,CA,94306,2/13/1983,5/14/2014,2.978,95
2
Koch,Sandra,2707 Waterview Lane,Apt 302,Las Vegas,NM,87701,6/6/1972,12/14/2015,2.546,69
Segmentation fault //occurs in while condition
Any help would be appreciated
I feel sick that I didn't see this right away.
int numLines = lineCount(inFile);
returns the correct number of lines in the file. Bug's not here, man.
std::string data[numLines];
Is not kosher C++, but will create an array with an element for every line in the file if supported. Your program is running, so it's supported. Still, prefer to use Library Containers.
Meanwhile in readFile...
while(std::getline(inFile, data[i]))
Will try to read a line into data[i]. Whether the read succeeds or not, there must be a data[i] to read into. There won't be for the last try.
The logic goes
read in line 1. Successful, so
read in line 2. Successful, so
read in line 3. Successful, so
read in line 4. Fail. But this does not keep getline from looking off the end of data for a string and going boom (specifically undefined behaviour that manifested as going boom) because there isn't one.
The right solution
int main(){
std::ifstream inFile("input.txt");
// no longer need. Vector keeps track for us
// int numLines = lineCount(inFile);
std::vector<std::string> data;
// read nothing from file. Don't need to rewind
readFile(inFile, data);
// note: files close themselves when they are destroyed.
//inFile.close();
return 0;
}
void readFile(std::istream& inFile, std::vector<std::string> & data){
int i = 0;
std::string line; // line to read into. Always there, so we don't have to worry.
while(std::getline(inFile, line)){
std::cout << i << "\n"; //testing values
std::cout << line << "\n"; //testing values
data.push_back(line); // stuff line into vector.
i++;
}
}
The No vector Allowed Solution
int main(){
std::ifstream inFile("input.txt");
int numLines = lineCount(inFile);
// legal in every C++, but prefer container may want some extra armour
// here to protect from numlines 0.
std::string * data = new std::string[numlines];
// the following is a faster way to rewind a file than closing and re-opening
inFile.clear(); // clear the EOF flag
inFile.seekg(0, ios::beg); // rewind file.
readFile(inFile, data);
inFile.close();
return 0;
}
void readFile(std::istream& inFile, std::string * data){
int i = 0;
std::string line; // same as above. line is here even if data[i] isn't
while(std::getline(inFile, line)){
std::cout << i << "\n"; //testing values
std::cout << line << "\n"; //testing values
data[i] = line; // stuff line into array. Smart compiler may realize it can move
//if not, c++11 adds a formal std::move to force it.
i++;
}
}
Related
I have the following code:
const char *fn = fileName.c_str();
std::ifstream file (fn);
std::vector<std::string> value(20000);
int i = 0;
while ( file.good() )
{
getline ( file, value[i] );
i+=1;
std::cout << value[i]<< std::endl;
std::cout << i << std::endl;
}
The program reads the whole file, I know this because the correct number of indexes are printed. However there is no data, just a new line before each printing of "i". This is a file that I have saved from excel in windows and am reading in Linux - Is this my issue? What happened to my data?
there is no data, just a new line before each printing of "i".
Because you increment i before accessing value[i].
Incrementing i just after accessing value[i] solves the problem of missing data.
DEMO
A better way to read in the file:
std::string text_line;
std::vector<string> file_lines;
while (std::getline(file, text_line))
{
file_lines.push_back(text_line);
}
Although not optimal speed-wise, it gets the job done and doesn't have an upper limit (except by the amount of memory your program is allowed).
Edit:
Sorry, I was simply fixing the logic error apparent.
However, here is an ideal version of reading lines of a file:
#include <iostream>
#include <fstream>
#include <vector>
int main() {
std::ifstream file {"test.txt"};
std::vector<std::string> values;
std::string temp;
while (getline(file, temp)) {
values.push_back(temp);
}
for (int i = 0; i < values.size(); ++i) {
std::cout << values[i] << '\n' << i << '\n';
}
}
I am trying to write a program where I read a text file and then take each line in the textfile and store them in a string vector. I think I am able to open the textfile however I noticed that after I open the textfile anything after that point does not execute. For example I have a cout statement at the end of my main function that outputs when I enter the name of a file that doesn't exist. However if I type in a file name does exists I get no output from the last cout statement. Anyone know why this is? Thanks!
int main()
{
vector<string>line;
string fileName = "test.txt";
ifstream myFile(fileName.c_str());
int i = 0;
int count = 0;
vector<string>lines;
cout << "test" << endl;
if (myFile.is_open())
{
cout << "test2" << endl;
while (!myFile.eof())
{
getline(myFile, lines[i],'\n');
i++;
}
myFile.close();
}
if (!myFile.is_open())
{
cout<< "File not open"<< endl;
}
myFile.close();
cout << "Test3" <<endl;
return 0;
}
Try this:
string fileName = "test.txt";
ifstream myFile(fileName); // .c_str() not needed - ifstream can take an actual string
vector<string> lines;
string line; // temporary variable for std::getline
while (getline(myFile, line)) {
lines.push_back(line); // use push_back to add new elements to the vector
}
As pointed out in the comments, the most likely reason that your program seems to "end" prematurely is that it's crashing. std::getline takes a reference-to-string as its second argument. In your code, your vector is empty; therefore lines[i] for any i returns a reference to invalid memory. When getline tries to access that memory, the program crashes.
If you want an exception thrown when you try to access an out-of-bounds index of a vector, use lines.at(i) instead of lines[i].
You need to use push_back() because your initial vector is empty and, you can not use indexes on empty vector. If you do so, it will leads to undefined behavior.
std::ifstream input( "filename.ext" );
std::vector<std::string> lines;
for( std::string line; getline( input, line ); )
{
lines.push_back(line);
}
I tried making a program earlier that tells the user then number of char, words, and lines in a text file. I made functions to determine the numbers of each, yet I was passing them by value. This resulted in an error since after reading the number of char it would be at the end of the file and then output zero for the other two. Now I cant seem to rewrite my functions so that the file is open and closed each time its checked for char, words, and lines. Any one see where my errors are?? Thanks! (just copied and pasted one of my functions for now).
int num_of_lines(ifstream file)
{
string myfile;
myfile = argv[1];
ifstream l;
l.open(myfile);
int cnt3 = 0;
string str;
while(getline(file, str))cnt3++;
l.close();
return(cnt3);
}
int main(int argc, char **argv)
{
int num_of_char(ifstream file);
string file;
file = argv[1];
if(argc == 1)die("usage: mywc your_file");
ifstream ifs;
ifs.open(file);
if(ifs.is_open())
{
int a, b, c;
a = num_of_lines(ifs);
cout <<"Lines: " << a << endl;
}
else
{
cerr <<"Could not open: " << file << endl;
exit(1);
}
ifs.close();
return(0);
}
There is no way to "reopen" a file other than knowing the name and creating a new ifstream, but you can use the seekg member function to set your read position in the file, and setting it to 0 will have the next read operation start from the beginning of the file.
A stream is not possible to copy, so you can't pass it "by value", but must pass it by reference.
int num_of_lines(ifstream &file)
{
int count = 0;
string str;
while (getline(file, str)) {
count++;
}
file.seekg(0);
return count;
}
For the full problem, I agree with Mats Petersson, though. Counting both characters, lines and words in one pass will be much more efficient than reading through the file three times.
i wrote a code in C++ where it opens a .txt file and reads its contents, think of it as a (MAC address database), each mac address is delimited by a (.), my problem is after i search the file for total number of lines , iam unable to return the pointer to the initial position of the file in here i use seekg() and tellg() to manipulate the pointer to the file.
here is the code:
#include <iostream>
#include <fstream>
#include <conio.h>
using namespace std;
int main ()
{
int i = 0;
string str1;
ifstream file;
file.open ("C:\\Users\\...\\Desktop\\MAC.txt");
//this section calculates the no. of lines
while (!file.eof() )
{
getline (file,str1);
for (int z =0 ; z<=15; z++)
if (str1[z] == '.')
i++;
}
file.seekg(0,ios::beg);
getline(file,str2);
cout << "the number of lines are " << i << endl;
cout << str2 << endl;
file.close();
getchar();
return 0;
}
and here is the contents of the MAC.txt file:
0090-d0f5-723a.
0090-d0f2-87hf.
b048-7aae-t5t5.
000e-f4e1-xxx2.
1c1d-678c-9db3.
0090-d0db-f923.
d85d-4cd3-a238.
1c1d-678c-235d.
here the the output of the code is supposed to be the first MAC address but it returns the last one .
file.seekg(0,ios::end);
I believe you wanted file.seekg(0,ios::beg); here.
Zero offset from the end (ios::end) is the end of the file. The read fails and you're left with the last value you read in the buffer.
Also, once you've reached eof, you should manually reset it with file.clear(); before you seek:
file.clear();
file.seekg(0,ios::beg);
getline(file,str2);
The error would have been easier to catch if you checked for errors when you perform file operations. See Kerrek SB's answer for examples.
Your code is making all sorts of mistakes. You never check any error states!
This is how it should go:
std::ifstream file("C:\\Users\\...\\Desktop\\MAC.txt");
for (std::string line; std::getline(file, line); )
// the loop exits when "file" is in an error state
{
/* whatever condition */ i++;
}
file.clear(); // reset error state
file.seekg(0, std::ios::beg); // rewind
std::string firstline;
if (!(std::getline(file, firstline)) { /* error */ }
std::cout << "The first line is: " << firstline << "\n";
If I include the if test in my code the error message is returned and I'm not sure why.
and when it's not used, my program get's stuck in a loop where it never reaches the end of the file. I don't understand what's going wrong.
int countlines()
{
fstream myfile;
myfile.open("questions.txt", ios::in);
string contents;
int linenumber = 0;
//if (myfile.is_open())
// {
while (!myfile.eof())
{
getline( myfile, contents );
if (contents != "")
{
linenumber++;
}
}
cout << "there are " << linenumber << " lines.\n";
//}else {cout<<"Unable to get file.\n";}
myfile.close();
return(linenumber);
}
What's going on is that your file is not being opened. That's why is_open fails.
Then, when you comment out the check, you're breaking your loop because you're iterating incorrectly (see my comment) and not detecting stream failures (.eof() will never be true on that stream).
Make sure that the file is in the right place, and that it is accessible.
The correct idiom for reading a file line-by-line in C++ is using a loop like this:
for (std::string line; std::getline(file,line);)
{
// process line.
}
Inserting this in your example (+fixing indentation and variable names) gives something like this:
int countlines(const std::string& path)
{
// Open the file.
std::ifstream file(path.c_str());
if (!file.is_open()) {
return -1; // or better, throw exception.
}
// Count the lines.
int count = 0;
for (std::string line; std::getline(file,line);)
{
if (!line.empty()) {
++count;
}
}
return count;
}
Note that if you don't intend to process the line contents, you can actually skip processing them using std::streambuf_iterator, which can make your code look like:
int countlines(const std::string& path)
{
// Open the file.
std::ifstream file(path.c_str());
if (!file.is_open()) {
return -1; // or better, throw exception.
}
// Refer to the beginning and end of the file with
// iterators that process the file character by character.
std::istreambuf_iterator<char> current(file);
const std::istreambuf_iterator<char> end;
// Count the number of newline characters.
return std::count(current, end, '\n');
}
The second version will completely bypass copying the file contents and avoid allocating large chunks of memory for long lines.
When using std::istream and std::ostream (whose std::fstream implements), the recommended usage is to directly use the stream in a bool context instead of calling eof() function because it only return true when you managed to read until the last byte of the file. If there was any error before that, the function will still return true.
So, you should have written your code as:
int countlines() {
ifstream myfile;
int linenumber = 0;
string linecontent;
myfile.open("question.txt", ios::in);
while (getline(myfile, linecontent)) {
if (!linecontent.empty()) {
++linenumber;
}
}
return linenumber;
}
Try the following code. It will also (hopefully) give you an idea why the file open is failing...
int countlines()
{
ifstream myfile;
myfile.open("questions.txt");
string contents;
int linenumber = 0;
if (myfile.is_open())
{
while (getline(myfile, contents))
{
if (contents != "")
linenumber++;
}
cout << "there are " << linenumber << " lines." << endl;
myfile.close();
}
else
cout << "Unable to get file (reason: " << strerror(errno) << ")." << endl;
return linenumber;
}