I am attempting to check if a file exists, and then if so proceed with a task and if not to just output that there is no such file. I have done this in other code but it doesn't seem to be working with my current code.
The basics of it read:
count=argc;
for(i=0; i < count-1; i++)
{
filename[i] = argv[i+1];
}
for( i=0; i < count-1; i++)
{
int tempi=i;
ifstream infile(filename[i].c_str());
if(infile)
{
//do things
}
else
{
cout<<"no file"<<endl;
}
You need to call infile.is_open(). Also, do you plan to do something with the file if it exists, or not?
The canonical way to access argv is:
int main( int argc, char * argv[] ) {
for ( int i = 1; i < argc; i++ ) {
// do something with argv[i]
}
}
infile, in the conditional, evaluates to false when the stream is in a "bad" state.
However, merely failing to open a file does not leave the stream in a bad state (welcome to C++!). Only after attempting to read from the stream would this mechanism work for you.
Fortunately, you can use infile.is_open() to explicitly test for whether the stream was opened or not.
Edit
The above is not true.
Testing the stream state is sufficient, and I can't see anything wrong with your code.
Related
For convenience, I have renamed all the files to simple names for my example.
I'm trying to run an executable (test.exe), with a C++ entrypoint int main(int argc, char* argv[]) from a batch file (test.bat), and pass arguments from a text file (test.txt). The end goal is to run unit tests on an SDK using the testing software (test.exe).
My issue is that I do not want to have to use a variable when I call the executable since it makes the code harder to read :
rem This works
set /p test_input=<test.txt& call test.exe %test_input%
After some research, I figured I should use input redirection like so :
rem This does not work
call test.exe < test.txt
This does not work, and I don't understand why.
This is what I initially tried, and it has been suggested before on SO (here).
I have access to the test.exe code, so I can print argc and argv :
int main(int argc, char* argv[])
{
if(new_argc >= 2)
{
if(strcmp("-help", argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < argc; i++)
{
if(strcmp("-framerate", argv[i]) ==0)
{
i++;
if(i < argc)
{
FrameRate = (float)atof(argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
}
If I enter the arguments and parameters manually, it works as expected.
test.txt
-arg1 param1 -arg2 param2 ...
test.bat
call test.exe < test.txt
Output : test.exe runs as if there are no arguments or parameters.
Edit :
Added a few details about the entrypoint and renamed the batch variable.
Thanks to the comments under my question, I was pushed in the right direction.
The problem was my understanding of <. It literally means "Read file to STDIN" (as mentionned here). Many other documentation sites give vague definitions like (as mentionned here)
command < filename : Type a text file and pass the text to command
I need to parse the input correctly, since stdin isn't available in argc or argv, but through std::cin.
My batch code and text file remain unchanged, and I want to maintain the same form of parsing to avoid rewriting multiple projects, so I split the input string using the Solution 1.3 from here (slightly modified) and created a new_argv.
std::vector<char*> split(const std::string& s, char delimiter)
{
std::vector<char*> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(_strdup(token.c_str()));
}
return tokens;
}
int main(int argc, char* argv[])
{
std::string extra_input; // Variable to store the contents of test.txt
std::getline(std::cin, extra_input); // Recuperate the contents of test.txt
std::vector<char*> new_argv = split(extra_input, ' '); // Split the args
for(int i = argc - 1; i >= 0; i--)
new_argv.insert(new_argv.begin(), argv[i]); // Add the original args to the beginning
const size_t new_argc = new_argv.size(); // Create the new argc based on the final argument list (vector)
if(new_argc >= 2)
{
if(strcmp("-help", new_argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < new_argc; i++)
{
if(strcmp("-framerate", new_argv[i]) ==0)
{
i++;
if(i < new_argc)
{
FrameRate = (float)atof(new_argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
// Important, don't forget to free the memory used by the _strdup
for(int i=1; i < new_argc; i++)
{
if(i >= argc)
free(new_argv[i]);
}
}
test.bat
call test.exe < test.txt
test.txt
-arg1 param1 -arg2 param2 ...
Of course, I need to add some checks to make it properly handle whitespace, but that's the gist of it. Thank you for your help and external point of view.
Edit : Fixed a mistake in the code.
void graph::fillTable()
{
ifstream fin;
char X;
int slot=0;
fin.open("data.txt");
while(fin.good()){
fin>>Gtable[slot].Name;
fin>>Gtable[slot].Out;
cout<<Gtable[slot].Name<<endl;
for(int i=0; i<=Gtable[slot].Out-1;i++)
{
**//cant get here**
fin>>X;
cout<<X<<endl;
Gtable[slot].AdjacentOnes.addFront(X);
}
slot++;
}
fin.close();
}
That's my code, basically it does exactly what I want it to but it keeps reading when the file is not good anymore. It'll input and output all the things I'm looking for, and then when the file is at an end, fin.good() apparently isn't returning false. Here is the text file.
A 2 B F
B 2 C G
C 1 H
H 2 G I
I 3 A G E
F 2 I E
and here is the output
A
B
F
B
C
G
C
H
H
G
I
I
A
G
E
F
I
E
Segmentation fault
-
Here's is Gtable's type.
struct Gvertex:public slist
{
char Name;
int VisitNum;
int Out;
slist AdjacentOnes;
//linked list from slist
};
I'm expecting it to stop after outputting 'E' which is the last char in the file. The program never gets into the for loop again after reading the last char. I can't figure out why the while isn't breaking.
Your condition in the while loop is wrong. ios::eof() isn't
predictive; it will only be set once the stream has attempted
(internally) to read beyond end of file. You have to check after each
input.
The classical way of handling your case would be to define a >>
function for GTable, along the lines of:
std::istream&
operator>>( std::istream& source, GTable& dest )
{
std::string line;
while ( std::getline( source, line ) && line.empty() ) {
}
if ( source ) {
std::istringstream tmp( line );
std::string name;
int count;
if ( !(tmp >> name >> count) ) {
source.setstate( std::ios::failbit );
} else {
std::vector< char > adjactentOnes;
char ch;
while ( tmp >> ch ) {
adjactentOnes.push_back( ch );
}
if ( !tmp.eof() || adjactentOnes.size() != count ) {
source.setstate( std::ios::failbit );
} else {
dest.Name = name;
dest.Out = count;
for ( int i = 0; i < count; ++ i ) {
dest.AdjacentOnes.addFront( adjactentOnes[ i ] );
}
}
}
}
return source;
}
(This was written rather hastily. In real code, I'd almost certainly
factor the inner loop out into a separate function.)
Note that:
We read line by line, in order to verify the format (and to allow
resynchronization in case of error).
We set failbit in the source stream in case of an input error.
We skip empty lines (since your input apparently contains them).
We do not modify the target element until we are sure that the input
is correct.
One we have this, it is easy to loop over all of the elements:
int slot = 0;
while ( slot < GTable.size() && fin >> GTable[ slot ] ) {
++ slot;
}
if ( slot != GTable.size )
// ... error ...
EDIT:
I'll point this out explicitly, because the other people responding seem
to have missed it: it is absolutely imperative to ensure that you have
the place to read into before attempting the read.
EDIT 2:
Given the number of wrong answers this question is receiving, I would
like to stress:
Any use of fin.eof() before the input is known to fail is wrong.
Any use of fin.good(), period, is wrong.
Any use of one of the values read before having tested that the input
has succeeded is wrong. (This doesn't prevent things like fin >> a >>
b, as long as neither a or b are used before the success is
tested.)
Any attempt to read into Gtable[slot] without ensuring that slot
is in bounds is wrong.
With regards to eof() and good():
The base class of istream and ostream defines three
“error” bits: failbit, badbit and eofbit. It's
important to understand when these are set: badbit is set in case of a
non-recoverable hardward error (practically never, in fact, since most
implementations can't or don't detect such errors); and failbit is set in
any other case the input fails—either no data available (end of
file), or a format error ("abc" when inputting an int, etc.).
eofbit is set anytime the streambuf returns EOF, whether this
causes the input to fail or not! Thus, if you read an int, and the
stream contains "123", without trailing white space or newline,
eofbit will be set (since the stream must read ahead to know where the
int ends); if the stream contains "123\n", eofbit will not be set.
In both cases, however, the input succeeds, and failbit will not be
set.
To read these bits, there are the following functions (as code, since I
don't know how to get a table otherwise):
eof(): returns eofbit
bad(): returns badbit
fail(): returns failbit || badbit
good(): returns !failbit && !badbit && !eofbit
operator!(): returns fail()
operator void*(): returns fail() ? NULL : this
(typically---all that's guaranteed is that !fail() returns non-null.)
Given this: the first check must always be fail() or one of the
operator (which are based on fail). Once fail() returns true, we
can use the other functions to determine why:
if ( fin.bad() ) {
// Serious problem, disk read error or such.
} else if ( fin.eof() ) {
// End of file: there was no data there to read.
} else {
// Formatting error: something like "abc" for an int
}
Practically speaking, any other use is an error (and any use of good()
is an error—don't ask me why the function is there).
Slightly slower but cleaner approach:
void graph::fillTable()
{
ifstream fin("data.txt");
char X;
int slot=0;
std::string line;
while(std::getline(fin, line))
{
if (line.empty()) // skip empty lines
continue;
std::istringstream sin(line);
if (sin >> Gtable[slot].Name >> Gtable[slot].Out && Gtable[slot].Out > 0)
{
std::cout << Gtable[slot].Name << std::endl;
for(int i = 0; i < Gtable[slot].Out; ++i)
{
if (sin >> X)
{
std::cout << X << std::endl;
Gtable[slot].AdjacentOnes.addFront(X);
}
}
slot++;
}
}
}
If you still have issues, it's not with file reading...
The file won't fail until you actually read from past the end of file. This won't occur until the fin>>Gtable[slot].Name; line. Since your check is before this, good can still return true.
One solution would be to add additional checks for failure and break out of the loop if so.
fin>>Gtable[slot].Name;
fin>>Gtable[slot].Out;
if(!fin) break;
This still does not handle formatting errors in the input file very nicely; for that you should be reading line by line as mentioned in some of the other answers.
Try moving first two reads in the while condition:
// assuming Gtable has at least size of 1
while( fin>>Gtable[slot].Name && fin>>Gtable[slot].Out ) {
cout<<Gtable[slot].Name<<endl;
for(int i=0; i<=Gtable[slot].Out-1;i++) {
fin>>X;
cout<<X<<endl;
Gtable[slot].AdjacentOnes.addFront(X);
}
slot++;
//EDIT:
if (slot == table_size) break;
}
Edit: As per James Kanze's comment, you're taking an adress past the end of Gtable array, which is what causes segfault. You could pass the size of Gtable as argument to your fillTable() function (f.ex. void fillTable(int table_size)) and check slot is in bounds before each read.
*Edited in response to James' comment - the code now uses a good() check instead of a
!eof() check, which will allow it to catch most errors. I also threw in an is_open()
check to ensure the stream is associated with the file.*
Generally, you should try to structure your file reading in a loop as follows:
ifstream fin("file.txt");
char a = '\0';
int b = 0;
char c = '\0';
if (!fin.is_open())
return 1; // Failed to open file.
// Do an initial read. You have to attempt at least one read before you can
// reliably check for EOF.
fin >> a;
// Read until EOF
while (fin.good())
{
// Read the integer
fin >> b;
// Read the remaining characters (I'm just storing them in c in this example)
for (int i = 0; i < b; i++)
fin >> c;
// Begin to read the next line. Note that this will be the point at which
// fin will reach EOF. Since it is the last statement in the loop, the
// file stream check is done straight after and the loop is exited.
// Also note that if the file is empty, the loop will never be entered.
fin >> a;
}
fin.close();
This solution is desirable (in my opinion) because it does not rely on adding random
breaks inside the loop, and the loop condition is a simple good() check. This makes the
code easier to understand.
I am writing a short program to sort an array of integers. I am having trouble opening my input file which is "prog1.d". The assignment has asked to create a symbolic link in the programs directory, and I after creating the object & executable, we invoke the program as follows...
prog1.exe < prog1.d &> prog1.out
I know my bubble sort works correctly & efficiently because I have used my own test 'txt' file.
The assignment says:
Your program gets the random integers from stdin and puts them in an array, sorts the integers in the array in ascending order, and then displays the contents of the array on stdout.
How do I read the file using 'cin' until EOF & add the integers to my array a[] ?
Here is my code so far:
int main( int argc, char * argv[] )
{
int a[SIZE];
for ( int i=1; i<argc; i++)
{
ifstream inFile; // declare stream
inFile.open( argv[i] ); // open file
// if file fails to open...
if( inFile.fail() )
{
cout << "The file has failed to open";
exit(-1);
}
// read int's & place into array a[]
for(int i=0; !inFile.eof(); i++)
{
inFile >> a[i];
}
inFile.close(); // close file
}
bubbleSort(a); // call sort routine
printArr(a); // call print routine
return 0;
}
I know that opening a stream is the wrong way to do this, I just was using it for a test 'txt' file I was using to make sure my sorting worked. The teacher said we should redirect the input to 'cin' like someone was entering integers on a keyboard.
Any help would be greatly appreciated.
When you're using redirection on the command line, argv does not contain the redirection. Instead, the specified file simply becomes your stdin/cin. So you don't need to (and shouldn't try to) open it explicitly -- just read from the standard input, as you would read from the terminal when input isn't redirected.
Since you are piping the file on the stdin, you don't have the file name on argv[1], just read the stdin as the user was typing at the console, for example using cin:
cin.getline (...);
The other answers are completely correct, but here's the rewritten code to claify:
int main( int argc, char * argv[] )
{
int a[SIZE];
int count = 0;
// read int's & place into array a[]
//ALWAYS check the boundries of arrays
for(int i=0; i<SIZE; i++)
{
std::cin >> a[i];
if (std::cin)
count = count + 1;
else
break;
}
bubbleSort(a, count); // call sort routine
printArr(a, count); // call print routine
return 0;
}
As everyone has stated, use std::cin directly -- you don't need to open the input file, your shell has already done that for you.
But, please, please, please, don't use cin.eof() to test to see if you have reached the end of your input. If your input is flawed, your program will hang. Even if your input isn't flawed, your program may (but won't necessarily) run the loop one extra time.
Try this loop instead:
int a[SIZE];
int i = 0;
while( std::cin >> a[i]) {
++i;
}
Or, add robustness by using std::vector which will automatically grow:
std::vector<int> a;
int i;
while(std::cin >> i) {
a.push_back(i);
}
Or, use generic algorithms:
#include <iterator>
#include <algorithm>
...
std::vector<int> a;
std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(a));
Hey.
I have some problems writing char to a file with ofstream.
this is how the code looks (Just to show how it works. This is NOT the real code).
char buffer[5001];
char secondbuffer[5001];
char temp;
ifstream in(Filename here);
int i = 0;
while(in.get(secondbuffer) && !in.eof[])
{
i++;
}
for(int j = 0; j < i; j++)
{
secondbuffer[j] = buffer[j];
}
ofstream fout(somefile);
fout << secondbuffer;
// end of program
The problem is that it reads the characters of the first file fine, but when it writes to the second file, it adds all characters from the first file, as its supposed to do, but when there are no more characters, it adds a lot of "Ì" characters in the end of file.
fx:
file 1:
abc
file 2:
abcÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ...
How can I prevent the program save "Ì" in the file?
EDIT2:
int i = 0;
lenghtofFile++;
while(fin.get(firstfileBuffer[i]) && !fin.eof())
{
i++;
lenghtofFile++;
}
firstfileBuffer[i] = '\0';
for(int j = 0; j < lenghtofFile; j++)
{
if(secondfileBuffer[j] != ' ' && secondfileBuffer[j] != '\0')
{
secondfileBuffer[j] = function(key, firstfileBuffer[j]);
}
}
secondfileBuffer[lenghtofFile]='\0';
fout << secondfileBuffer;
You need to null-terminate secondbuffer. You are adding all the characters read from the stream, which do not include the trailing NULL.
on the line before fout, add
secondbuffer[j]='\0\';
The problem is that there is no terminating null character in your file. When you read the file in, you get "abc" just fine, but the garbage that was sitting in secondbuffer when it was declared is still there, so writing "abc" to the beginning of it means that you have a 5001-length array of garbage that starts with "abc."
Try adding
secondbuffer[i] = '\0'; after your for loop.
This should work fine:
#include<iostream>
#include<fstream>
using namespace std;
int main()
{
char buffer[5001];
char secondbuffer[5001];
ifstream in("foo.txt", ifstream::in);
ofstream fout("blah_copy.txt");
do
{
in.getline(buffer,5001);
fout<<buffer;
}
while(!in.eof());
in.close();
fout.close();
return 0;
}
I'm writing a program for an exercise that will read data from a file and format it to be readable. So far, I have a bit of code that will separate a header from the data that goes under it. Here it is:
int main() {
ifstream in("records.txt");
ofstream out("formatted_records.txt");
vector<string> temp;
vector<string> headers;
for (int i = 0; getline(in,temp[i]); ++i) {
static int k = -1;
if (str_isalpha(temp[i])) {
headers[++k] = temp[i];
temp.erase(temp.begin() + i);
}
else {
temp[i] += "," + headers[k];
}
}
}
(str_isalpha() is just a function that applies isalpha() to every character in a string.) Now, the for-loop in this program doesn't execute, and I can't figure out why. Does anybody know?
EDIT: As suggested, I changed it to
string line;
for (int i = 0; getline(in,line); ++i) {
temp.push_back(line);
Still skips the for-loop altogether.
vector<string> temp; makes an empty vector. When you then try to read into temp[0], that is undefined behavior. You should pass as getline's second argument a separate string variable, say string foo; before the loop, then temp.push_back(foo); as the first instruction in the loop's body.
If the loop still doesn't run after ensuring that you're reading into a valid string reference, then you should check that the stream you're reading from is valid. The stream will be invalid if the file doesn't exist or if you lack permission to read it, for instance. When the stream isn't valid, getline won't read anything. Its return value is the same stream, and when converted to bool, it evaluates as false. Check the stream's status before proceeding.
ifstream in("records.txt");
if (!in.is_open()) {
std::cerr << "Uh-oh.\n";
return EXIT_FAILURE;
}