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".
Related
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.
I've decided to start learning C++ before I take a formal class on it next year, I've begun with some of the easy challenges on CodeEval and Project Euler. In this one, you have to take an input file which has strings of words, and you have to output the line of the file with the words reversed. Such that a file with the following input
1: This is line one
2: This is line two
would end up as
1: one line is This
2: two line is This
I wrote the following program to do that, and aside from not properly reversing the string, instead fully reversing the word, it Segmentation faults despite compiling with no errors or warnings. I assume I've missed something about proper memory management in C++ but I'm not sure what it is. So can someone enlighten me on what I've missed in this regarding memory management?
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
int main(int argc, char** argv)
{
std::string filename = argv[1]; //has to be argv[1], argv[0] is program name
std::string output_string; //final output
std::string line; //Current line of file
std::ifstream read(filename.c_str());
if(read.is_open()){
while(std::getline(read,line)){
std::string temp;
std::istringstream iss;
iss.str(line);
while(iss >> temp){ //iterates over every word
output_string.insert(0,temp); //insert at the start to reverse
output_string.insert(0," "); //insert spaces between new words
}
output_string.erase(0,1); //Removes the space at the beginning
output_string.insert(0,"\n"); //Next line
}
output_string.erase(0,1); //Remove final unnecessary \n character
read.close();
}
else{
std::cout<<"Unable to open file\n";
}
for(unsigned int i = output_string.length(); i>=0;i--){
std::cout<<output_string[i];
}
std::cout<<"\n";
}
for(unsigned int i = output_string.length(); i>=0;i--){
std::cout<<output_string[i];
}
The segfault happens here; you might be able to get a warning from the compiler with some additional flags. e.g. g++ produces no warnings with -Wall, but produces two warnings with -Wextra: one about argc not being used, and the other about this loop never terminating.
The issue here is twofold: as Captain Giraffe said, you're starting beyond the actual length your strings; but also the condition i >= 0 will always be true, because i is unsigned. Therefore once it reaches 0, the next decrement will cause it to wrap around to the highest possible value, and then you definitely get an out-of-bounds memory access.
The warning reported is:
reverse.cpp:31:49: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
for(unsigned int i = output_string.length(); i>=0;i--){
Also as Captain Giraffe said, you're reversing the entire file, not just each line. You can therefore just reverse each line and output it once you've finished the line, rather than storing the entire output for later.
Here's the entire program with minimal changes to avoid any warnings and get the correct output. The main change is moving all usage of output_string into the read loop.
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "Need a file to process!" << std::endl;
return 1;
}
std::string filename = argv[1]; //has to be argv[1], argv[0] is program name
std::string line; //Current line of file
std::ifstream read(filename.c_str());
if(read.is_open()){
while(std::getline(read,line)){
std::string output_string; //final output
std::string temp;
std::istringstream iss;
iss.str(line);
while(iss >> temp){ //iterates over every word
output_string.insert(0,temp); //insert at the start to reverse
output_string.insert(0," "); //insert spaces between new words
}
output_string.erase(0,1); //Removes the space at the beginning
std::cout << output_string << std::endl;
}
read.close();
}
else{
std::cout<<"Unable to open file\n";
}
}
Change the last for-statement to
std::cout << output_string;
You are starting off the output by printing the character after the last in the output string. This gets rid of the segfault. Now you are trying to print the reversed output in reverse.
Now you find that you should just reverse each line, not the entire text. You can easily do that by adding a starting index for each line, instead of 0, in your inserts.
So instead of
output_string.insert(0,temp); //insert at the start to reverse
You can do
output_string.insert(start_of_line, temp); //insert at the start to reverse
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.
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;
}
I'm trying to read character by character from a text file until EOF, put them into a character array, so that I can manipulate it after. Compiled with g++ without errors, and when run, I'm prompted for the input file but then it just hangs.
int main (int argc, char *argv[]) {
string filename;
ifstream infile;
char *cp, c[1024];
memset (c, 0, sizeof(c));
cp = c;
cout << "Enter file name: " << endl;
cin >> filename;
//open file
infile.open( filename.c_str() );
//if file can't open
if(!infile) {
cerr << "Error: file could not be opened" << endl;
exit(1);
}
while (!infile.eof()); {
infile.get(c, sizeof(infile));
// get character from file and store in array c[]
}
}//end main
You should try the istream::read() method rather than get(). This will help resolve any buffer overruns:
unsigned int chars_read = 0;
//...
// Read in the file.
if (!infile.read(c, sizeof(c))
{
// Handle the read error here.
// Also check for EOF here too.
}
// Obtain the number of characters actually read.
chars_read = infile.gcount();
First off, you don't want to test for eof()! Somehow I start to feel like Don Quixote having found my windmills. However, I do know that you need to check that the input was successful after trying to read it because before attempting to read the stream can't know whether it will be successful.
You program actually doesn't hang! It just waits for you to enter sizeof(infile) characters or end the input (e.g., using Ctrl-D on UNIXes and Ctrl-Z on Windows). Of course, this may look remarkable like a hanging program. You can verify that this is, indeed, the problem by using a smaller size, e.g., 4. Of course, sizeof(infile) is nearly as good as a small random number: It is the size of an object of type std::ifstream and who can tell what that is? You probably meant to use sizeof(c) to make sure that the call to get(c, n) won't write more character than can fit into c.
Try this:
int cont = 0;
while(infile.good()) {
c[cont++] = infile.get();
}