Why am I getting a seg fault with my if statement? - c++

I want to read a file and count the words. I want it set up so I can use a command line or, if no file is entered on the command line, to trigger an if statement that will get the file name and read it, then count the words. It works if I type the file name in command line, however it seg faults if I don't use it. Here is the code:
int main(int argc, char **argv)
{
char file[75];
if (argc < 2)
{
cout << "Please enter the filename: ";
cin >> file;
strcpy(argv[1], file);
}
string content;
ifstream inFile(argv[1]);
int count = 0;
while (inFile >> content)
count++;
inFile.close();
display(count, argv);
return 0;
}

You should not modify data of argv especially out of bounds. You logic should work opposite:
char file[75];
if (argc < 2)
{
cout << "Please enter the filename: ";
cin >> file;
} else
strcpy( file, argv[1] );
string content;
ifstream inFile(file);
but you better use std::string for variable file as well.
Also cin >> inputs only words (excluding space symbols) but filenames can have them, so you better use cin.getline( file ) or std::getline( cin, file ) if you change file to std::string

If the condition is true that is if argc is indeed less than 2 then in general case argc is equal to 1 and according to the C Standard argv[argc] is equal to NULL.
So the program in this case has undefined behavior.
In any case it is a bad idea to copy a string to argv[n] where n is some index because the source string can be larger than the target string.
You could use a reverse approach that is to copy argv[1] to file.

Related

Reading in from file gives unexpected output

I am working on reading in from a file and parsing through data from command line argument for homework. And I ran in a wall and I do not know what's the problem, and I hope I could get some advice on what I am missing.
The data file is composed thusly; on the first line, it has number of total lines. For each line after that, it is a line of string separated by | character. I need the '|' character because I want to split my string into substrings.
Here is an example of input file.
3
league of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
Here is my code.
int main( int argc, char* const argv[] )
{
//change string to char* so I can check through each char to see if the
//thing I read in is '|' character.
String Data = (argv[1]);
ifstream fin (Data.c_str());
//check whether the file is open.
if ( !fin.is_open() )
{
cout << "Could not open file" << endl;
}
else
{
int dataLines;
char dataBuffer[100];
//The first integer I read in will be how many lines I will loop through
fin >> dataLines;
//ignore the new line character and do not include it in the count of
//dataLines.
fin.ignore();
//use noskipws so I can recognize whitespaces.
fin >> noskipws >> dataBuffer;
//TEST CODE: COMMENTED OUT FOR NOW.
//cout<<dataBuffer<<endl;
//loop for the number of lines
for(int i = 0; i < dataLines; i++)
{
fin.getline(dataBuffer, 100);
//print the buffer for checking
cout<<dataBuffer<<endl;
}
}
//close the file.
fin.close();
return 0;
}
The result is supposed to look like this.
league of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
The actual result looks like this
of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
The first word that I read in from buffer is gone. "league" is the one that is missing, and I tried to see what the problem is by inserting the test code at the location specified in my code. With the given test code, my output is
league
of legends|Teemo|Master Yi|Vayne
apple|samsung|smart phone|smart watch
overwatch|d.va|junkrat|Reinhart
So the problem is that between reading in the file with noskipws and the forloop that loops over dataLine. Before the forloop my buffer is league. Yet once I enter the loop it is passed that and goes straight to of.
What am I missing here? What could be a possible solution?
Main problem:
fin >> noskipws >> dataBuffer;
Does two things. 1. >> noskipws turns off automatically skipping whitespace, unnecessary because of how OP is reading the stream. 2. >> dataBuffer reads the first word from the stream, in this case consuming the word "league"
Solution: Don't do this.
Other problems:
fin.ignore();
will ignore exactly one character. But what if someone left a nigh-invisible space after the count? Instead use
fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
to ensure the rest of the line is consumed in its entirity.
char dataBuffer[100];
Why make yourself suffer? Instead use
std::string dataBuffer;
Recommendation:
Use std::stringstream and std::getline to tokenize the lines on '|'
std::stringstream stream(databuffer);
std::string token;
while (std::getline(stream, token, '|')
{
std::cout << token << ',';
}
You do not need the following line:
fin >> noskipws >> dataBuffer;
Tested with g++ 4.8.3 2 on RHEL 7.1
Thanks User 4581301. It reads in the data correctly and split with '|' character. Now I can work on storing the data into classes.
for anyone who may have same problem, this is the fixed up version of code.
int main( int argc, char* const argv[] )
{
String Data = (argv[1]);
ifstream fin (Data.c_str());
if ( !fin.is_open() )
{
cout << "Could not open file" << endl;
}
else
{
int dataLines;
char dataBuffer[100];
fin >> dataLines;
fin.ignore();
for(int i = 0; i < dataLines; i++)
{
while(fin.getline(dataBuffer, 100, '|'))
{
cout<<dataBuffer<<endl;// check to see if it reads in correctly.
}
}
}
fin.close();
return 0;
}

Fixing syntax of number of line reading function

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.

Why does my countlines function always return 0?

So I am making a program for a simple calendar app which reads in inputs from a file input.csv (its a text file with two columns which are seperated using commas and new lines for each command).
The first thing i want to do is count the number of lines from the input file, which is passed as the third argument in the command line, so I can make an array to hold each line separately but the function countLines always returns 0!
Project code:
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
//Prototypes
int countLines (ifstream& countfiles);
int countLines(ifstream& countfile)
//counts number of lines in file passed to function
{
string line;
int numberOfLines;
numberOfLines = 0;
//reads through each line until end of file
while(getline(countfile, line))
{
numberOfLines++;
}
return numberOfLines;
}
int main (int argc, char* argv[])
{
if(argc != 3) cout << "Usage: calendar.out datafile inputfile";
//Create input streams to both files
ifstream apptsfp;
ifstream inputfp;
//Open streams to both files
apptsfp.open(argv[2]);
inputfp.open(argv[3]);
int numberOfInputs=0;
numberOfInputs = countLines(inputfp)-1;
cout << "number of input commands: " << numberOfInputs << endl;
return 0;
}
Almost certainly because you are failing to open your input file.
inputfp.open(argv[3]);
if (!inputfp.is_open())
{
cerr << "failed to open input file " << argv[3] << '\n';
return 1;
}
Files can fail to open for all sorts of reasons, you should always check for this.
BTW don't use an array to hold the input lines, use std::vector<std::string>. Then you can use push_back to add the lines to the vector. This will be easier and more efficient because you won't have to read the file twice. What more could you ask for!
std::vector<std::string> lines;
std::string line;
while (getline(inputfp, line))
lines.push_back(line);
It seems you only want two arguments, not three as you say in the question (the "first" argument is the program name). This means that the input file is in argc[2] instead, and argv[3] is a NULL pointer.
This means that your open call will fail, but you do not check for that.
Your access to argv[3] is incorrect. The second file name (third arg, including the program name in arg[0]) is in slot 2 (the array is zero-based).
Try:
apptsfp.open(argv[1]);
inputfp.open(argv[2])
You are trying to access argv[3] which is null. Try this :-
int main (int argc, char* argv[])
{
if(argc != 3)
cout << "Usage: calendar.out datafile inputfile";
//Create input streams to both files
ifstream apptsfp;
ifstream inputfp;
//Open streams to both files
apptsfp.open(argv[1]);
inputfp.open(argv[2]);
int numberOfInputs=0;
numberOfInputs = countLines(inputfp)-1;
cout << "number of input commands: " << numberOfInputs << endl;
return 0;
}

Cannot extract data from command line as expected

I have a courses0.dat file with a single 4 on line 1 that I want to extract with my ifstream program:
void processEnrollments (std::istream& courseFile);
int main (int argc, char** argv)
{
// Take input and output file names from the command line
ifstream coursesIn (argv[1]);
return 0;
}
void processEnrollments (istream& courseFile)
{
int numCourses;
courseFile >> numCourses;
cout << numCourses;
// Create the arrays we need
//!! Insert your code here
}
when I run
program courses0.dat
my test is cout'ing a 32767 instead of a 4. My .dat file is in the same directory as my executable.
any clue as to what is going on?
thanks
Check for errors! Try to use the full path to the file when you pass it as an argument.
My guess is courseFile >> numCourses; fails because ifstream coursesIn (argv[1]) doesn't find or can't access the file.
Try this
if( courseFile >> numCourses )
cout << numCourses;
Does it output anything then?

Cin.get() after file is opened with ifstream

I posted a similar code earlier, but I think this is a different issue now. I just can't figure out why my run code won't go past "infile open". ("-e" prints out, "-d" doesn't) I'm trying to open my file, use a command line option to determine if I will print out a certain multiple of characters.
For example, a.out -d 2 < sample.txt will print out every second letter.
int main (int argc, char *argv[])
{
ifstream infile;
if (infile.good())
printf("infile open \n");
int c;
int number = 0;
int count = 0;
string str1 = argv[1];
string str2 = "-d";
string str3 = "-e";
if (str1.compare(str2) == 0)
{
printf("entered -d\n");
c = infile.get();
while(!infile.eof()) {
number = atoi(argv[2]);
if (count == number)
{
cout.put(c);
count = 0;
}
else
count++;
c = infile.get();
}//end while
}//end if
if (str1.compare(str3) == 0)
printf("entered -e\n");
}//end main
infile is never opened:
ifstream infile; // Does not open a file as no file name is supplied.
Either use cin or pass "sample.txt" as another command-line argument and open it:
ifstream inFile(argv[3]);
if (inFile.is_open())
{
}
Other points:
Use std::cout instead of mixing printf() and std::cout.
atoi() returns 0 if the argument is invalid or if the argument is a valid 0. See strtol() for an alternative.
There is no reason to convert argv[2] on every iteration of the while. Just do it once prior the while.
Always check argc before accessing the elements of argv, to avoid invalid memory access.
std::string instances can be compared using operator==.
When running something like this on command line: "a.out < sample.txt", there is no actual filename specified to open, the "<" command in Unix will just make the contents of sample.txt be passed to a.out via standard input...therefore, like hmjd pointed out, you should use cin. You would want to physically open a file if it was supplied as an argument, i.e. "a.out sample.txt" as opposed to "a.out < sample.txt".