Sending text file name from command line c++ - c++

I want to run my code and send my file in 2 ways
myprogram < input.txt or cat input.txt | myprogram
myprogram input.txt
I have figured out the secong way using argc and argv[] but I am not able to figure out how to write the code for the first option.
int main (int argc, char *argv[])
{
ifstream fin;
if(argc > 1){
fin.open (argv[1]);
}
else
}

As mentioned above in the comment, a portable way is passing either the open file or std::cin as an istream reference to a function and doing your input there. In that case either the file or std::cin may be passed. E.g.
#include <iostream>
#include <fstream>
#include <string>
void readinfo (std::istream& in)
{
std::string s;
while (in >> s)
std::cout << s << '\n';
}
int main (int argc, char **argv) {
if (argc > 1) { /* read from file if given as argument */
std::ifstream fin (argv[1]);
if (fin.is_open())
readinfo (fin);
else {
std::cerr << "error: file open failed.\n";
return 1;
}
}
else { /* read from stdin */
readinfo (std::cin);
}
return 0;
}
A non-portable Linux only option reading from /dev/stdin if no file is given simply requires a ternary operator, e.g.
std::ifstream fin (argc > 1 ? argv[1] : "/dev/stdin");
if (!fin.is_open()) {
std::cerr << "error: file open failed.\n";
return 1;
}
/* read from fin here */
Neither are completely elegant, but both support (subject to the OS constraint)
myprogram < input.txt
or
myprogram input.txt

You want to read from stdin, and for that there's 2 options:
std::cin
fread() and other C-style IO
std::cin >> https://en.cppreference.com/w/cpp/io/cin has the advantage of reading formatted text into some binary representation https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt
C-style IO https://en.cppreference.com/w/cpp/io/c has the advantage of reading binary data well.
It depends on what you want to do with it your input

Related

Make a program work with input and a file

I am making a shell interpreter which works only with keyboard input, but I have to make it work with a text files.
int main(int argc, char *argv[], char *envp[]) {
string comando;
mi_argv[0] = NULL;
int pid_aux;
el_prompt = "$> ";
if(argv[1] != NULL)
{
ifstream input(argv[1]);
if (!input)
{
cerr << "No se reconoce el archivo " << argv[1] << endl;
exit(EXIT_FAILURE);
}
}
while(!cin.eof())
{
cout << el_prompt;
getline(cin, comando);
...
}
}
The point is to make this work with a file like argument ./shell file.txt. I tried to redirect the file to cin, but I don't know how to do it.
Put the code reading from the input stream in a separate function, and pass a reference to the input stream to the function. Then you can pass any input stream to the function, a file you have opened or std::cin.
Like e.g.
void main_loop(std::istream& input)
{
// Use `input` here like any other input stream
}
Now you can call the function either with standard input
main_loop(std::cin);
or your file
main_loop(input);
Also, be careful with that loop condition, doing while (!stream.eof()) will in most cases not work as expected. The reason being that the eofbit flag is not set until after you try to read from beyond the end of the stream, and will lead to the loop being run one extra time.
Instead do e.g. while (std::getline(...)).
Something like this.
#include <string>
#include <iostream>
#include <fstream>
void usage() {
std::cout << "Usage: shell <filename>\n";
}
int main(int argc, char* argv[]) {
std::string comando;
std::string el_prompt = "$> ";
if (argc != 2) {
usage();
exit(1);
}
std::ifstream input(argv[1]);
while (std::getline(input, comando)) {
std::cout << el_prompt << comando;
}
}
You would need code of course to parse the command and execute it.

Is there an elegant way to determine if an ifstream is attached to stdin?

My program is a common shell that I am trying to write in C++. In addition to taking commands from the command line it has to be able to read commands in a file - the file name being passed as an optional arg, not by redirection.
If the arg is present I open the passed filename otherwise I open "/dev/stdin". I'm not thrilled about opening the dev file and it isn't my main question but if someone has a better method I would love to hear it.
Eventually I have to read commands given to the shell but first I must present the prompt if it I am reading from stdin or skip the prompt if the input is coming from a file. My question is: is there a better way to determine in getCommand if the input stream is stdin than declaring a global or passing a bool or similar hacks?
It occurs to me that if I can somehow use std::cin rather than opening the /dev file I can pass the stream as an istream. Would that make it easier to distinguish between the two? E.G. if (source == cin) ?
Thanks for any and all suggestions.
bool getCommand(ifstream source, std::string command)
{
if (source == stdin)
//print prompt to stdout
// do the rest of stuff
return true;
}
int main(int argc, char *argv[])
{
std::ifstream input;
std::string command;
if (argc == 2)
{
input.open(argv[1], std::ifstream::in);
if (! input)
{
perror("input command file stream open");
exit(EXIT_FAILURE);
}
}
else
{
input.open("/dev/stdin", std::ifstream::in);
if (! input)
{
perror("input stdin stream open");
exit(EXIT_FAILURE);
}
}
//.......
if (getCommand(input, command))
//.......
}
If you use std::istream& as the type of your variable then you can use std::cin instead of opening /dev/stdin:
std::ifstream fileinput;
if (argc == 2)
{
fileinput.open(argv[1], std::ifstream::in);
if (! fileinput)
{
perror("input command file stream open");
exit(EXIT_FAILURE);
}
}
std::istream &input = (argc == 2) ? fileinput : std::cin;
The remainder of your question should be about identifying a tty (Detect if stdin is a terminal or pipe?). But failing that, once you've made the above change you could compare with the address of std::cin:
if (&input == &std::cin) {
std::cout << "prompt:";
}
Obviously that won't identify the case where the command line argument is /dev/stdin, so it's a bit of a bodge. But I'd probably argue that's actually an improvement given the funny requirement, because someone can at least write yourprogram /dev/stdin < input.txt, or sort input.txt | yourprogram /dev/stdin in order to work around your restriction and avoid the prompt :-)
First off, if you want to read either from std::cin or from a std::ifstream, I would implement the work in terms of an std::istream and certainly avoid opening /dev/stdin:
std::istream in(std::cin.rdbuf());
std::ifstream fin;
if (argc == 2) {
fin.open(av[1]);
// check that the file is actually open or produce an error
in.rdbuf(fin.rdbuf());
}
else {
// determine if you need to create a prompt
}
if (getCommand(in, command)) {
...
}
Now, to determine if you actually need to write a prompt it isn't sufficient to determine if you are reading from standard input but you also need to determine if the standard input is connected to something with a screen and a keyboard. There is no portable way to do so, however. On UNIXes there are functions like isatty() which can be used to determine if file descriptor 0 is connected to a tty. I would use this information to set up an iword() which can then be used to check if the stream need a prompt:
static int const needs_prompt = std::ios_base::xalloc();
...
in.iword(needs_prompt) = isatty(0);
...
if (in.iword(needs_prompt)) {
std::cout << "pompt>";
}
If you want to get fancy you can create a custom std::streambuf which is hooked up with an std::ostream which is tie()ed to the input stream stream and writes a prompt upon sync(): Every time a read operation on an input stream is done the tie()ed std::ostream is flushed. However, this requires that you only read once for each prompt being produced, e.g. using std::getline(in, line). Obviously, if you don't need a prompt, you don't tie() an std::ostream. Below is a simple program demonstrating the auto-prompt approach (without doing any of the other business):
#include <iostream>
#include <streambuf>
struct prompt
: std::streambuf
{
std::string d_prompt;
std::ostream& d_out;
prompt(std::string const& p, std::ostream& out)
: d_prompt(p)
, d_out(out)
{
}
int sync() {
this->d_out << this->d_prompt << std::flush;
return 0;
}
};
int main()
{
prompt p("prompt>", std::cout);
std::ostream helper(&p);
std::cin.tie(&helper);
for (std::string line; std::getline(std::cin, line); ) {
std::cout << "read '" << line << "'\n";
}
}

Text file I/O with fstream and ifstream

#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
int main(int argc, char *argv[])
{
ifstream is;
is.open(argv[1]);
ofstream outfile;
outfile.open(argv[2]);
char ch;
while (1)
{
ch = is.get(); // this is where test.txt is supposed
outfile.put(ch); // to be copied to test2.txt
if (is.eof())
break;
cout << ch; //this shows
}
is.close();
outfile.close();
ifstream outfile2;
outfile2.open(argv[2]);
char ch2;
while (1)
{
ch2 = outfile2.get();
if (outfile2.eof())
break;
cout << ch2; //this doesnt
}
outfile2.close();
system("PAUSE");
return 0;
}
I run it through cmd giving it 2 arguments test.txt test2.txt and it outputs what i have written in test.txt in cmd but test2.txt remains empty for some reason?
Please check the streamstate not just for eof() but also for failure. Also, after reading the last character, it wouldn't be uncommon if the streamstate was EOF even though the character was successfully read. Therefore, always try to read an element and if it succeeded, and only then, use the element:
ifstream in(argv[1]);
ofstream out(argv[2]);
char c;
while(in.get(c))
out.put(c);
To make this really efficient, use this though:
out << in.rdbuf();
In any case, check the streamstate for success:
if(!in.eof())
throw std::runtime_error("failed to read input file");
if(!out.flush())
throw std::runtime_error("failed to write output file");
for me its not coming blank but with some extra appended characters. this is because you are writing the character which you got from the old file to the new one before checking eof().
the code for writing from one file to another should be changed as
while (1)
{
ch = is.get();
if (is.eof())
break;
outfile.put(ch);
cout << ch; //this shows
}

Using input redirection, how to read file and get character string in C?

I have a file that I want my program to read from using input redirection from the command line. For example,a.out < file.dat . Then I was going to use cin.get() and put characters in an array.
I don't want to hard code any input file names, which I have been seeing in some of the existing posts. If I treat this input redirection as stdin, do I have to explicitly open my file?
int main (int argc, char *argv[])
{
string filename;
ifstream infile;
cin >> filename;
do {
int c = 0;
c = infile.get(); //need to get one character at a time
//further process
} while ( ! infile.eof());
}
You can just use cin, which is a stream buffer associated with stdin
#include <iostream>
int main()
{
char c;
while (std::cin.get(c))
{
std::cout << c << std::endl; // will print out each character on a new line
}
exit(0);
}

File Input in C++

I've been searching the internet for a while, but all I can find for file input in C++ is when you know the filename.
I'm trying to write a program to perform an addition of 2 numbers that are greater than 0 from a file, but without using scanf or cin. I want to load the file into memory, but all of the code I can find in regards to this situation requires knowledge of the filename. The file is formatted with 2 integers on a single line, separated by a space, and there are multiple lines of integers. The program will output the sum of the two numbers. I can easily do this with scanf, but if I were given a massive file, I would want to load it into memory (save mapping for later).
Loading the file into memory is giving me trouble, because I do not know the filename, nor how to find out, unless the user inputs the name of the file (not going to happen). I want the program to be executed like so, but using the most raw, and basic forms of C++ IO:
./myprog < boatloadofnumbers
How would I start my program to take the whole "boatloadofnumbers" as a file, so I can use more basic functions like read()? (also, what is the above method called? passing input?)
int main(){
int a,b;
while (scanf("%i,%i",&a,&b)>-1){
printf("%i\n",(a+b));
} //endwhile
return 0;
} //endmain
When the program is called as you state, then the content of boatloadofnumbers can be read from std::cin.
This method is called input redirection and is done by the shell, not your program.
Wiht input redirection the shell usually buffers the content of the file. That's a quite fast way to stream a file a single time through a computation.
It's not entirely clear how you're going to read a file when you don't know the filename. Presumably you don't know the filename at compile-time. That's okay, you can get this from the command-line at runtime, like this:
./myprog boatloadofnumbers
Then your filename is in argv[1] and you can access it using a std::ifstream.
If you're being given the input directly on stdin via redirection (such as ./myprog < boatloadofnumbers) you don't need a filename at all, you can just use std::cin.
The following main() will deal with both of these situations:
int main(int argc, char* argv[])
{
if (argc == 2)
{
std::cerr << "Reading from file" << argv[1] << std::endl;
std::ifstream ifs(argv[1]);
if (ifs)
{
sum_lines(ifs);
}
else
{
std::cerr << "Could not read from " << argv[1] << std::endl;
}
}
else
{
std::cerr << "Reading from stdin" << std::endl;
sum_lines(std::cin);
}
}
A sample sum_lines() may look a bit like this:
void sum_lines(std::istream& is)
{
int first = 0, second = 0;
std::string line = "";
while (std::getline(is, line))
{
std::istringstream iss(line);
if (is >> first >> second)
{
std::cout << first << " + " << second << " = " << first + second << std::endl;
}
else
{
std::cerr << "Could not parse [" << line << "]" << std::endl;
}
}
}
This doesn't care from where the input comes, so you can easily inject a std::istringstream for unit-testing. Also, this doesn't read the whole file into memory, just one line at a time, so it should deal with averybigboatloadofnumbers.
With shell redirection, your program can read from the standard input, which may be desirable. However, it may also be desirable to read from a file. It's easy to support both:
cat data > ./prog
./prog < data
./prog -f data
The first two are similar, and the contents of the file data are available from the program's standard input; the third line simply passes a command-line argument. Here's how we support this:
#include <cstdio>
#include <cstring>
void process_input(std::FILE * fp)
{
char buf[4];
std::fread(buf, 4, 1, fp);
// ...
}
int main(int argc, char * argv[])
{
std::FILE * fp = stdin; // already open!
if (argc >= 3 && 0 == std::strcmp(argv[1]. "-f"))
{
fp = std::fopen(argv[2], "rb");
if (!fp)
{
std::fprintf(stderr, "Could not open file %s.\n", argv[2]);
return 1;
}
}
process_input(fp);
if (fp != stdin) { std::fclose(fp); }
}
Equivalently, you can achieve something similar with iostreams, though it's a bit more roundabout to have a nice, universal reference:
#include <fstream>
int main()
{
std::ifstream ifp;
if ( /* as before */ )
{
ifp.open(argv[2], std::ios::binary);
if (!ifp) { /* error and die */ }
}
std::ifstream & infile = ifp ? ifp : std::cin;
process_input(infile);
}