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++
Related
I've been googling this question for a few hours and can't seem to find anything that addresses it.
I'm reeaaaally hazy on file operations in C++, but I've spent about 20 of the last 36 hours reading documentation and forum questions trying to get a project for a friend together.
Say I've got a file called raw_questions.txt, and I'd like to make some changes to it. This file is a study guide for an exam, and has a question followed by 4 multiple-choice answers. I want to remove blank lines and add some tokens to allow another program I'm working on to parse it. I've written a formatter program to perform those operations. The operations are:
Remove blank lines from source file as it appears it's double-spaced
Add a delimiter character ('#') to the end of each question and
answer.
Using the delimiter, read each question and answer in as a string
and append it to an output file with a token at the beginning of
question or answer, which will let my other program know whether a
line contains a question or answer.
My question: I'm stuck at how to move from one operation to the next. My current approach is to read each line into a string, perform the operation on the string, and to add the new string to an output file. Using this approach, to perform the next operation I have to open the previous operation's output file as my new input file, and make a new output file for that operation. I feel like there's got to be a better way, but like I said, I'm pretty hazy on file operations in C++. What should I be doing in this situation?
I've considered creating an ifstream and ofstream that both point to the same file, and hoping that when the ifstream file is opened, it will store a temporary copy in memory. Then, after I read line by line and write to my ofstream object, when it closes it will overwrite my old file. I don't know if that makes any sense, and I don't think that's even how fstream works.
The code that I have so far:
#include <fstream>
#include <string>
#include "Debug.h"
Debug debugger;
void remove_empty_lines (std::ifstream& input, std::ofstream& output);
void insert_delimiter (std::ifstream& input, std::ofstream& output, char delimiter);
void create_output (std::ifstream& input, std::ofstream& output);
int main() {
debugger.set_active();
char delimiter = '#';
std::ifstream input;
std::ofstream output;
input.open("questions_source.txt");
output.open("questions_intermidiate.txt");
remove_empty_lines (input, output);
}
void remove_empty_lines (std::ifstream& input, std::ofstream& output) {
while (!input.eof()) {
std::string line;
std::getline(input, line);
if (line != "") {
output << line << std::endl;
}
}
}
void insert_delimiter(std::ifstream& input, std::ofstream& output) {
}
// This function doesn't quite work, WIP - Please ignore
void create_output(std::ifstream& input, std::ofstream& output) {
std::string line;
for (int i = 1; !input.eof(); i++) {
debugger.out("Inserting tokens.");
bool found = false;
while (!found) {
getline (input, line);
if (i < 10) {
if (line[1] == ')') {
line.erase (0, 3);
output << "[" << i << "]" << line << std::endl;
debugger.out("Found line: " + line);
found = true;
}
} else if (i < 100) {
if (line[2] == ')') {
line.erase (0, 4);
output << "[" << i << "]" << line << std::endl;
debugger.out("Found line: " + line);
found = true;
}
}
}
for (int j = 0; j < 4; j++) {
getline (input, line);
if (line[1] == ')') {
line.erase (0, 3);
output << "[" << i << "a]" << line << std::endl;
}
}
}
}
I'm also trying to teach myself git at the moment, so I happen to have the project I'm working on hosted on github here. I don't know if the context will make what I'm trying to do make sense, but I'm posting it just in case.
Bonus question: I've been racking my brain, but I haven't come up with a solution to adding the delimiter. Answers seem to be one line long, so I can probably just add the delimiter to the end of any line starting with "A)" etc., but some of the questions are much longer. My thought is to find any occurrence of "A)" and add the delimiter to the end of the line above it, but I can't think of how to do that. Can anyone point me in the right directions for member functions of fstream that might help?
Thanks for reading.
Streams do not magically read the entire file into memory. If that is what you want to do, you should just do that: my guess is that your file is considerably smaller than your available memory, and it might be easier to perform all the operations in place using standard C++ containers.
I have some code here
https://github.com/Fallauthy/Projects/blob/master/cPlusPlusProjects/bazaPracownikow/bazaPracownikow/bazaPracownikow/main.cpp
And I have no idea how to show contents in my file. I mean i know how, but it doesn't show same I Have in file (in link). It show in next line. This code is responsible to load file
while (!baseFile.eof()) {
//wczytaj zawartosc pliku do zmiennej
std::string buffer;
baseFile >> buffer;
//wypisz
loadLineFromBase += buffer;
loadLineFromBase += " \n";
}
std::cout << loadLineFromBase << std::endl;
Unless I see all your code all I can do for you is give you a sample in return, I don't know what you're trying to do but it seems in this case you're looking for this.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string Display = "";
ofstream FileOut;
ifstream FileInput;
FileOut.open("C:\\Example.txt");
FileOut << "This is some example text that will be written to the file!";
FileOut.close();
FileInput.open("C:\\Example.txt");
if (!FileInput)
{
cout << "Error File not Found: " << endl;
return 1;
}
while (!FileInput.eof())
{
getline(FileInput, Display);
}
FileInput.close();
cout << Display << endl;
return 0;
}
Simply put if you're currently working wit ha text document
use getline()
When you use getline() it takes two arguments the first will be in this case your ifstream object, as in what you're using to open the file. The second will be the string you're using to store the contents in.
Using the method I outlined above you'll be able to read the entire file contents.
And please next time as it was said above outline your problem more in depth and if you provide us with all of your code we may better assist you!
Your snippet of code automatically add a newline to every string read from the input file, even if originally those were words separeted by spaces. Probably you want to keep the structure of the original file, so it's better to read one line at a time and, unless you need it for some other uses, print it out in the same loop.
std::string buffer;
// read every line of baseFile till EOF
while ( std::getline(baseFile, buffer) ) {
std::cout << buffer << '\n';
}
I need to read a jpg file to a string. I want to upload this file to our server, I just find out that the API requires a string as the data of this pic. I followed the suggestions in a former question I've asked Upload pics to a server using c++ .
int main() {
ifstream fin("cloud.jpg");
ofstream fout("test.jpg");//for testing purpose, to see if the string is a right copy
ostringstream ostrm;
unsigned char tmp;
int count = 0;
while ( fin >> tmp ) {
++count;//for testing purpose
ostrm << tmp;
}
string data( ostrm.str() );
cout << count << endl;//ouput 60! Definitely not the right size
fout << string;//only 60 bytes
return 0;
}
Why it stops at 60? It's a strange character at 60, and what should I do to read the jpg to a string?
UPDATE
Almost there, but after using the suggested method, when I rewrite the string to the output file, it distorted. Find out that I should also specify that the ofstream is in binary mode by ofstream::binary. Done!
By the way what's the difference between ifstream::binary & ios::binary, is there any abbreviation for ofstream::binary?
Open the file in binary mode, otherwise it will have funny behavior, and it will handle certain non-text characters in inappropriate ways, at least on Windows.
ifstream fin("cloud.jpg", ios::binary);
Also, instead of a while loop, you can just read the whole file in one shot:
ostrm << fin.rdbuf();
You shouldn't read the file to a string because it is legal for a jpg to contain values that are 0. However in a string, the value 0 has a special meaning (it's the end of string indicator aka \0). You should instead read the file into a vector. You can do this easily like so:
#include <algorithm>
#include <iostream>
#include <fstream>
#include <vector>
int main(int argc, char* argv[])
{
std::ifstream ifs("C:\\Users\\Borgleader\\Documents\\Rapptz.h");
if(!ifs)
{
return -1;
}
std::vector<char> data = std::vector<char>(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
//If you really need it in a string you can initialize it the same way as the vector
std::string data2 = std::string(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>());
std::for_each(data.begin(), data.end(), [](char c) { std::cout << c; });
std::cin.get();
return 0;
}
Try opening the file in binary mode:
ifstream fin("cloud.jpg", std::ios::binary);
At a guess, you were probably trying to read the file on Windows and the 61st character was probably 0x26 -- a control-Z, which (on Windows) will be treated as marking the end of the file.
As far as how to best do the reading, you end up with a choice between simplicity and speed, as demonstrated in a previous answer.
I am writing a C++ program to read an exe file. I wrote it and I test it on a text file instead of exe file. it was true.
when I test it with an exe file I understand that my exe file have 0x00 value in it (not at its end). so my while loop stop before end of file because I used:
class A{
private:
ifstream myFile;
void Read(char *filename)
};
void A::Read(char *str)
{
myFile.open(str,ios::binary);
while (!myFile.eof())
{
InputFile.get(ch);
myString.push_back(ch);
}
}
what should I do? if I should use size of the file, how can i get it?
You must open the file stream with the std::ios::binary mode flag.
As James McNellis pointed out you need to open the file in binary mode: Try something like the following:
#include <iostream>
#include <fstream>
int main()
{
std::ifstream in("main.obj", std::ios_base::binary);
std::streamsize bytes_read = 0;
if (in.is_open())
{
while (!in.eof())
{
char buf[1024];
// Use unformatted read.
in.read(buf, 1024);
if (in.gcount() > 0)
{
// The first 'in.gcount()' chars in
// 'buf' were read.
bytes_read += in.gcount();
}
}
}
in.close();
std::cout << "bytes read=" << bytes_read << "\n";
return 0;
}
EDIT:
Example modified to use get():
#include <iostream>
#include <fstream>
int main()
{
std::ifstream in("main.obj", std::ios_base::binary);
std::streamsize bytes_read = 0;
if (in.is_open())
{
while (!in.eof())
{
in.get();
if (1 == in.gcount())
{
bytes_read++;
}
}
}
in.close();
std::cout << "bytes read=" << bytes_read << "\n";
return 0;
}
Tested and works correctly.
In addition to opening the file in binary mode, the current code has a subtle bug which will cause the last character in the file to be read twice. The problem is that the myFile.eof() call does not do what you think it does. It does not tell you when you're at the end of the file. It tells you that you have tried to read beyond the end of the file. The idiomatic way to write a read-until-eof loop in C++ is:
while (myFile.get(ch))
myString.push_back(ch);
get returns an istream reference which, in this context, is implicitly convertible to bool and is used to indicate that there is no more data to read.
Only a hunch here, but my suspicion is that you're actually reading the whole file correctly, but measuring it wrong.
File reading (with binary mode) won't stop on a 0-byte, but there are several string related methods that will.
For example, you can't measure the size of a binary "blob" using strlen(), you can't copy it using strcpy().
Without seeing the actual way you're storing and measuring the data, it's hard to see where things go wrong, but I strongly suspect that you're actually reading the whole file correctly if you're using binary mode.
I found my mistake, The program read all the bytes but I cout that bytes in a vector<char>, So it's obvious I saw just bytes before 0x00.
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);
}