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);
}
Related
I have an assignment that wants plain text data to be read in from a file, and then outputted to a separate binary file. With that being said, I expect to see that the contents of the binary file not to be intelligible for human reading. However, when I open the binary file the contents are still appearing as plain text. I am setting the mode like this _file.open(OUTFILE, std::ios::binary). I can't seem to figure out what I'm missing. I've followed other examples with different methods of implementation, but there's obviously something I'm missing.
For the purpose of posting, I created a slimmed down test case to demonstrate what I'm attempting.
Thanks in advance, help is greatly appreciated!
Input File: test.txt
Hello World
main.cpp
#include <iostream>
#include <fstream>
using namespace std;
#define INFILE "test.txt"
#define OUTFILE "binary-output.dat"
int main(int argc, char* argv[]) {
char* text = nullptr;
int nbytes = 0;
// open text file
fstream input(INFILE, std::ios::in);
if (!input) {
throw "\n***Failed to open file " + string(INFILE) + " ***\n";
}
// copy from file into memory
input.seekg(0, std::ios::end);
nbytes = (int)input.tellg() + 1;
text = new char[nbytes];
input.seekg(ios::beg);
int i = 0;
input >> noskipws;
while (input.good()) {
input >> text[i++];
}
text[nbytes - 1] = '\0';
cout << "\n" << nbytes - 1 << " bytes copied from file " << INFILE << " into memory (null byte added)\n";
if (!text) {
throw "\n***No data stored***\n";
} else {
// open binary file for writing
ofstream _file;
_file.open(OUTFILE, std::ios::binary);
if (!_file.is_open()) {
throw "\n***Failed to open file***\n";
} else {
// write data into the binary file and close the file
for (size_t i = 0U; i <= strlen(text); ++i) {
_file << text[i];
}
_file.close();
}
}
}
As stated here, std::ios::binary isn't actually going to write binary for you. Basically, it's the same as std::ios::out except things like \n aren't converted to line breaks.
You can convert text to binary by using <bitset>, like this:
#include <iostream>
#include <vector>
#include <bitset>
int main() {
std::string str = "String in plain text";
std::vector<std::bitset<8>> binary; // A vector of binaries
for (unsigned long i = 0; i < str.length(); ++i) {
std::bitset<8> bs4(str[i]);
binary.push_back(bs4);
}
return 0;
}
And then write to your file.
In simplest terms, the flag std::ios::binary means:
Do not make any adjustments to my output to aid in readability or conformance to operating system standards. Write exactly what I send.
In your case, you are writing readable text and the file contains exactly what you sent.
You could also write bytes that are unintelligible when viewed as text. In that case, your file would be unintelligible when viewed as text.
Let me start with saying that I'm around 3 days old in C++.
Ok to the main question, I have a file that spans multiple lines, and I'm trying to print one specific line repeatedly, which is subject to change arbitrarily by some other process.
Example file :
line0
line1
somevar: someval
line3
line4
I'm trying to print the middle line (one that starts with somevar). My first naive attempt was the following where I open the file, loop through the contents and print the exact line, then move to the beginning of the file.
#include <iostream>
#include <fstream>
#include <string>
int main (int argc, char *argv[])
{
std::string file = "input.txt";
std::ifstream io {file};
if (!io){
std::cerr << "Error opening file" <<std::endl;
return EXIT_FAILURE;
}
std::string line;
std::size_t pos;
while (getline (io, line))
{
pos = line.find_first_of(' ');
if (line.substr (0, pos) == "somevar:")
{
// someval is expected to be an integer
std::cout << std::stoi( line.substr (pos) ) ) << std::endl;
io.seekg (0, std::ios::beg);
}
}
io.close();
return EXIT_SUCCESS;
}
Result : Whenever the file's updated, the program exits.
I came to think the fact that the IO I'm performing is actually buffered, therefore updating the file shouldn't reflect in our existing buffer just like that (this isn't shell scripting). So now I thought let's open and close the file on each iteration, which should effectively refresh the buffer every time, I know not the best solution, but I wanted to test the theory. Here's the new source :
#include <iostream>
#include <fstream>
#include <string>
int main (int argc, char *argv[])
{
std::string proc_file = "input.txt";
std::ifstream io;
if (!io){
std::cerr << "Error opening file" <<std::endl;
return EXIT_FAILURE;
}
std::string line;
std::size_t pos;
while (io.open(proc_file, std::ios::in), io)
{
io.sync();
getline (io, line);
pos = line.find_first_of(' ');
// The line starting with "somevar:" is always going to be there.
if (line.substr (0, pos) == "somevar:")
{
std::cout << std::stoi( line.substr (pos) ) ) << std::endl;
io.close();
}
}
io.close();
return EXIT_SUCCESS;
}
Result : Same as before.
What would be the ideal way of achieving what I'm trying to? Also, why's the program exiting whenever the file in question is being updated? Thanks (:
EDIT: The file I'm trying to read is "/proc/" + std::to_string( getpid() ) + "/io", and the line is the bytes read one (starts with read_bytes:).
As discovered in the comments, you are not reading a "real" file on disk, but rather /proc/PID/io which is a virtual file whose contents may only be determined when it is opened, thanks to VFS. Your statement that it can "change arbitrarily by some other process" is misleading, the file never changes, it simply has different content each time it is opened.
So now we know that no amount of seeking will help. We simply need to open the file afresh each time we want to read it. That can be done fairly simply:
char content[1000]; // choose a suitable value
const char key[] = "read_bytes:";
while (true)
{
std::ifstream io(io_filename);
if (!io.read(content, sizeof(content)))
break;
auto it = std::search(content, std::end(content), key, key + strlen(key));
std::cout << atoi(it + strlen(key)) << std::endl;
}
You should do something more careful than atoi() which won't stop at the end of the array, but I assume your real application will do something else there so I elided handling that.
The file I'm trying to read is some /proc/1234/io
That is the most important information.
Files in proc(5) are small pseudo-files (a bit like pipe(7)-s) which can only be read in sequence.
That pseudo file is not updated, but entirely regenerated (by the Linux kernel whose source code you can study) at every open(2)
So you just read all the file quickly in memory, and process that content in memory once you have read it.
See this answer to a very related question.... Adapt it to C++
I am currently working on a lossless compression algorithm using the Huffman technique.
I managed to compress the desired file, and save the compressed data to a file.
However, I am unable to correctly read what is recorded in this file. Preferably, I would like to store the contents of this file in a std::string.
Here is the content of this file:
00000L,1LP10LURD100LVRj1LLRQRER.Rm1LlRr1LiRe1000LpRdRn100Lv100LC1LARF1LIRNRbRcRa100Lo100Lh1Lx1LMRSRf1LgRqRs1LuRt
X6*ÃWØ¿¸u÷üwµS™’ð‚<)âóUO_mÁ9Õö/ë‰ÍÌ Ï-,SÁúÚâuçëðÒì`WVwÿƒüšÎ뉊?ÂgÿPÞuâ[CßTø¸CJŸy™“Þ¸Ý{+1sü <Ï~÷øà·\#¾¯à禡ú±Õö/Þüºû"í+ª•tÊæ+Ó¸Ð÷Õ>.'¦º¾Åü
úá‡
lÀ•¥¸Äq/?03òØ2'>÷?>9»ŸtY®Ùyù„‰u®'^~¿Û‚sŸ–öŽ(wß°/ì–~+K*•O´ ÿV:âyšö¨oãúü:ÿhrkã[‹7çjëĶ†KßW›˜iSêj£ÓúÆÉ×ûoÆÉï\l ÜKª‘Úɬ®b]T«ÏÖ42+4Ô2µ“í«Ç7•’Ðä×Æ·Ø÷+ìÊþ¸˜¡sü!xSC—7ëoÿø=_bÿÔÕG§ÃIöÕÐÊV¥:ÅÅ?]Yß„ËsòÖx×™øÞíàæÍÓ+˜¯~7Æ´
puQäöÕA}ÿ².Õ {vÓ+˜¯¾ÍÌ ŽÞ¹úb+
ç·ñd³óÿSUŸ
/ˆ®Œ|/õ}‹ñT`»áúüi|EuÄæçMSs’âóUOrßUSí
ïFòH{Tû8ð¹C«ì_ׯ?_†—ÄWÅæªöV’ŽÜc`>ö0Á‹þÚ8¡ß~ÃH;ÜÙ¸dY¿;þ8-\`ÈœûÜüÞþSS™Zäî#d÷®7Bûo}åʪ¿ŽŽ(wß°Òö?õø0¡¿¾¯àÕ׉hñÇ7·™Û‚rþ[ª%«KÅ’ý_býCxBí럿‡
löàœ„õÄææ÷îªÃÜ0ëðaxSˆ¬C´êÿm¨³]¯À¦W1^§T_XË®â6OF÷l4Ž;¦¿æ{÷»y—úØ«äý°sºâ³JÝB7ƶ…öñ«&ksóâóUOB ‘9÷¹ùÅf•º†ë^¹,«Ì÷ïCIÏÖ0Áÿ¯Á¿žšç壂{é„Eë"í¡–VK{åSÈ-Êjs"+
*}œx_“Þ¸Þ[·[ŸjÃBµN[êª}¡}’ÏÖ4<…^+PÞ‡16kø¼Õ^‡&ÁÓO.UUülñªY/dÅÏð€þ7O·?X×.„4Ÿm]2¹Š÷’œO©ªOúš¨ôøiSìãÂülžõÆòؽºÜûPÞªrßUSí?Õ~lÜ#_ÿ%›ŠÃIÏÖ,ïèÁwÂü4©¯ö¨oéZÁœSøªÿA¸—XõÂòO$Ìþ¸Äq>Ú¹¿[ÞY¬‹µòÑÄrÄÙ¯r†…öáæl ™Ÿ{Ÿ›&²¹Œº‹þÙÄíæ_ëBõb}çÃb8"ZW^Òº©Vɬ®bp¨±í¿Ê¥Sí³¯pȳ~vÎü ï–çç\LP¹þÛBûpÆó7\LP¹þ‚<)dÖW1d¾ë싶‹5Ûo3
Here is the code I wrote to read its contents:
int main(){
int number_of_lines = 0;
std::string line;
std::ifstream myfile("my_file.txt.huff");
while (std::getline(myfile, line)){
++number_of_lines;;
std::cout << "line number: " << number_of_lines << " content: " << line << std::endl;
}
}
I also tried via this way:
int main(){
FILE *find = fopen("my_file.txt.huff", "r");
int ca;
while(EOF != (ca=fgetc(find)))
std::cout << (char)ca;
std::cout << std::endl;
}
Here is the console rendering for the first code given:
line number : 1 content : 00000L,1LP10LURD100LVRj1LLRQRER.Rm1LlRr1LiRe1000LpRdRn100Lv100LC1LARF1LIRNRbRcRa100Lo100Lh1Lx1LMRSRf1LgRqRs1LuRt
line number : 2 content :
line number : 3 content : X6*├WÏ┐©Øu¸³wÁSÖÆé<)Ô¾UO_m┴9ı÷/Ùë═╠ ¤-,S┴·┌ÔuþÙÊý`WVw â³
At every time I try to read this file the problem is repeated. Indeed, the methods I use do not allow me to read it entirely.
Why does this problem occur?
thank you in advance
N.B: I was able to test, without success, the solution provided by Cillié Malan in this post. I'm having trouble for converting from a std::wstringstream to a std::string correctly.
Here is a short example that opens the file in binary mode and reads the entire file into a std::vector<uint8_t>. (you can also use a std::array<uint8_t> if you like). You open the file, .seekg() to the end, get the number of bytes and create the vector with that number of bytes and then .read() the file into the vector.
The following takes the filename as the first argument and outputs the content in hex format (for large files, change the output before testing to limit what is dumped to stdout)
#include <iostream>
#include <fstream>
#include <vector>
int main (int argc, char **argv) {
if (argc < 2) { /* validate filename given as argument */
std::cerr << "error: insufficient arguments\n"
"usage: " << argv[0] << " filename.bin\n";
return 1;
}
/* open file in binary mode, position at-the-end */
std::ifstream f (argv[1], std::ios::binary | std::ios::ate);
if (!f.is_open()) /* validate file open for reading */
return 1;
size_t nbytes = f.tellg(); /* get number of bytes in file */
f.seekg (0); /* rewind */
std::vector<uint8_t> arr(nbytes); /* declare vector with adequate storage */
f.read(reinterpret_cast<char*>(&arr[0]), nbytes); /* read file into vector */
if (f.bad() || f.fail()) /* validate read */
return 1;
for (auto& i : arr) /* output results (limit for larger files) */
std::cout << std::hex << std::showbase << static_cast<uint32_t>(i) << " ";
std::cout.put ('\n');
}
Look things over and let me know if you have further questions. There are several ways to approach this.
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
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;
}