I would like to translate the following C code to C++.
FILE *fp = NULL;
fp = fopen("./filename", "r");
int i = 0;
fscanf(fp, "%d\n", &i);
uint16_t j = (uint16_t) i;
This is what I came up with this:
ifstream file;
string filename = "./filename";
file.open(filename.c_str(), ios::in);
errno = 0;
if (file.fail()) {
int tmp = errno;
std::cout << file.c_str () << " not found: strerror(" << tmp << "): " << strerror(tmp) );
}
int i = 0;
file >> i >> std::endl;
uint16_t j = (uint16_t) i;
I would like to know whether the syntax is correct or improvable and more importantly whether it's safe against all kinds of inputs.
int read_int(const std::string file_name) {
std::ifstream file(file_name); //the file will close itself on destruction
std::uint16_t i;
//extract type, don't worry about what it is it will either compile or not
if(!(file >> i)) { //Catch failure
//or however you wish to deal with it.
throw std::runtime_error("can't read file");
}
return i;
}
int main() {
try{
std::uint16_t i=read_int("./filepath");
//do with i...
}
catch(const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Note if you do not have C++11 then you will need to use c_str() to open the file, but the string method is prefered.
EDIT: fstream close themselves, there is no need to close it yourself, the functionality is there incase you do have to do that however it is far better to rely on RAII semantics:
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
RAII dictates that you should open the file on construction and it will close on destructions, this ensures that there isn't any invalid (exclude EOF, file not found...) fstream object preventing bugs. RAII is a fundamental construct in C++ and should be used where ever resources are concerned.
The docs for the fstream destructors is here:
http://en.cppreference.com/w/cpp/io/basic_fstream
destructs the basic_fstream and the associated buffer, closes the file
The exact equivalent would be:
std::ifstream fs( "./filename" );
int i = 0;
fs >> i >> std::ws;
uint16_t j = i;
Whether this is what you really want is another question: the use of a
"\n" in the format string for fscanf suggests (to me, at least) that
you really want to read a single '\n', and not arbitrary white space;
what the "\n" means in fscanf, however, is to skip up to the next
non-whitespace. (In the case of interactive input, this can be a real
problem, since you won't return from your scanf—or my
replacement above—until you've encountered a non-white space
character or end of file. For input from a file, it may not be an
issue.)
When reading line oriented input, the classical solution is to use
std::getline, and then an std::istringstream to parse it.
Related
I'm trying to write a program that replaces a specific number with an 'x' character. The task requires every number to be in its own line, but it seems like '\n' is causing the read/write pointers to behave out of this world. Here's a picture of the output.
My questions are:
why are the pointers behaving this way?
How far do I need to move the write pointer backwards to overwrite a line to make this work?
is there an easier workaround?
Here's my code:
void input(int n)
{
fstream file;
file.open("numbers.txt", ios::out);
while(n --> 0)
{
file << n;
file << '\n';
}
file.close();
}
void read()
{
fstream file;
string tmp;
file.open("numbers.txt", ios::in);
while(true)
{
getline(file,tmp);
if(file.eof())
break;
cout << tmp << endl;
cout << "tellg: " << file.tellg() << " tellp: " << file.tellp() << endl;
}
file.close();
}
void replace()
{
fstream file;
string tmp;
file.open("numbers.txt", ios::in | ios::out);
while(true)
{
file >> tmp;
if(tmp == "6")
{
//cout << file.tellg() << endl;
file.seekp(file.tellg() - tmp.length()-1);
file << "x";
}
if(file.eof())
break;
}
file.close();
}
int main()
{
input(10);
replace();
read();
return 0;
}
Since you open your file in text mode, you need to account for the potential that the underlying stream may use a line end sequence (\r\n) rather than just a \n. I guess, this is the primary problem. The easiest remedy is probaly to open the file in binary mode:
file.open("numbers.txt", std::ios_base::binary | std::ios_base::in | std::ios_base::out);
That said, since you switch from writing to reading without intervening seek, your code is undefined behavior, i.e., anything can happen. You should seek to the current location between writing and reading.
Personally, I'd refrain from rewriting files in-place. It generally gets unnecessary trick. If I were to rewrite files in place, I'd use seekg() to get the current position before a read, saving the position and restoring it prior to the write (I essentially never use the seek operations, i.e., I may have got the signatures wrong):
for (std::streampos pos = (in >> std::ws).tellg();
in >> tmp; pos = (in >> ws).tellg()) {
if (need_to_overwrite) {
in.seekp(pos);
// ...
in.seekg(0, std::ios_base::cur);
}
}
The use of in >> std::ws is to make sure that whitespace is skipped before storing the position.
Also note that your check for file.eof() is wrong: the last line is processed twice. When reading from a file the result shall be tested before using the read string, e.g.:
while (in >> tmp) {
// ...
}
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.
I have N+1 files in a folder called b0.txt,b1.txt,b2.txt, ....,bN.txt.
I would like to open them inside a loop because for each of them I would like to copy the first 15 characters inside an array.
The code lines I wrote are basically:
int main(){
int N=4;
int i;
char number [15];
for(i=0; i< N; i++){
ifstream OpenFile("b%i.txt");
int l=0;
while(!OpenFile.eof()) {
OpenFile >> number [l];
l++;
}
OpenFile.close();
}
}
I'm using Dev C++ and when I compile these code lines no errors are shown. However, I'm not able to run the program.
Do you have any tip?
You should build the string name of the file. You might try:
char bufname[64];
snprintf(bufname, sizeof(bufname), b%i.txt", i);
ifstream OpenFile(bufname);
or use std::string or std::ostringstream tricks.
The filename "b%i.txt" is used explicitly as written, not as a printf-style format specifier.
You can either use sprintf, e.g.:
char filename[512];
sprintf(filename, "b%i.txt", i);
ifstream OpenFile(filename);
or use the C++ ostringstream, e.g.:
std::ostringstream filename;
filename << "b" << i << ".txt";
ifstream OpenFile(filename.str().c_str());
"b%i.txt" doesn't put i into the string... you can use:
std::ostringstream oss;
oss << 'b' << i << ".txt";
if (std::ifstream f(oss.str().c_str()))
...f is an open stream - use it here...
else
std::cerr << "couldn't open file " << oss.str() << '\n';
Don't test for eof - it doesn't work like that. Just use read and gcount:
if (OpenFile.read(number, sizeof number) && f.gcount() == sizeof number)
...you got the data...
else
std::cerr << "unable to read 15 characters from " << oss.str() << '\n';
(FWIW, eof is set after an input operation is concluded or aborted due to hitting eof, so it's not false before you attempt input, and it not being set doesn't guarantee the next operation will succeed - how could it if the stream doesn't yet know what you'll try to read?)
Your program does nothing to get the value of i into the string that you are sending to OpenFile(). You have to create a string with a textual representation of i embedded in it, which this code does not do.
Change
OpenFile("b%i.txt")
to
char filename[8];
sprintf(filename, "b%d.txt", i); // Create filename with embedded number
ifstream OpenFile(filename);
ifstream OpenFile("b%i.txt");
You're not writing in Batch! Here you can't put number variable using %name syntax.
If you're using C++11, I'd recommend to use std::to_string and type:
ifstream OpenFile("b"+std::to_string(i)+".txt");
for the code line where you are reading the characters in array
"OpenFile >> number [l];"
the compiler will through the error "segmentation fault" due to array out of bound.
so you have to add condition like this
if( l <= 14) // bcz you are starting with l=0
{
OpenFile >> number [l];
}
else
break;
i have the next code:
std::string line;
std::ifstream myfile ("text.txt");
if (myfile.is_open())
{
while ( myfile.good() )
{
getline (myfile,line);
std::cout << line << std::endl;
}
myfile.close();
}
is there a way to do it, and use char* instead of string?
Yes, if you really insist. There's a version of getline that's a member of std::istream that will do it:
char buffer[1024];
std::ifstream myfile("text.txt");
while (myfile.getline(buffer, sizeof(buffer))
std::cout << buffer << "\n";
myfile.close();
Note, however, that most C++ programmers would consider this obsolescent at best. Oh, and for the record, the loop in your question isn't really correct either. Using string, you'd typically want something like:
std::string line;
std::ifstream myfile("text.txt");
while (std::getline(myfile, line))
std::cout << line << "\n";
myfile.close();
or, you could use the line proxy from one of my previous answers, in which case it becomes simpler still:
std::copy(std::istream_iterator<line>(myfile),
std::istream_iterator<line>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
So you're looking for a more "C-like" solution?
#include<cstdio>
#define ENOUGH 1000
int main() {
char buffer[ENOUGH];
FILE* f = fopen("text.txt", "r");
while (true) {
if (fgets(buffer, ENOUGH, f) == NULL) break;
puts(buffer);
}
fclose(f);
return 0;
}
...plus some check whether the file was correctly opened. In this case, you use fgets() on the file f, reading into the char* buffer. However, buffer has only ENOUGH space allocated and this limit is also an important parameter to the fgets() function. It will stop reading the line when reaching ENOUGH - 1 characters, so you should make sure the ENOUGH constant is large enough.
But if you didn't mean to solve this in a "C-like" way, but are still going to use <iostream>, then you probably just want to know that the c_str() method of std::string returns the char* representation of that std::string.
If I include the if test in my code the error message is returned and I'm not sure why.
and when it's not used, my program get's stuck in a loop where it never reaches the end of the file. I don't understand what's going wrong.
int countlines()
{
fstream myfile;
myfile.open("questions.txt", ios::in);
string contents;
int linenumber = 0;
//if (myfile.is_open())
// {
while (!myfile.eof())
{
getline( myfile, contents );
if (contents != "")
{
linenumber++;
}
}
cout << "there are " << linenumber << " lines.\n";
//}else {cout<<"Unable to get file.\n";}
myfile.close();
return(linenumber);
}
What's going on is that your file is not being opened. That's why is_open fails.
Then, when you comment out the check, you're breaking your loop because you're iterating incorrectly (see my comment) and not detecting stream failures (.eof() will never be true on that stream).
Make sure that the file is in the right place, and that it is accessible.
The correct idiom for reading a file line-by-line in C++ is using a loop like this:
for (std::string line; std::getline(file,line);)
{
// process line.
}
Inserting this in your example (+fixing indentation and variable names) gives something like this:
int countlines(const std::string& path)
{
// Open the file.
std::ifstream file(path.c_str());
if (!file.is_open()) {
return -1; // or better, throw exception.
}
// Count the lines.
int count = 0;
for (std::string line; std::getline(file,line);)
{
if (!line.empty()) {
++count;
}
}
return count;
}
Note that if you don't intend to process the line contents, you can actually skip processing them using std::streambuf_iterator, which can make your code look like:
int countlines(const std::string& path)
{
// Open the file.
std::ifstream file(path.c_str());
if (!file.is_open()) {
return -1; // or better, throw exception.
}
// Refer to the beginning and end of the file with
// iterators that process the file character by character.
std::istreambuf_iterator<char> current(file);
const std::istreambuf_iterator<char> end;
// Count the number of newline characters.
return std::count(current, end, '\n');
}
The second version will completely bypass copying the file contents and avoid allocating large chunks of memory for long lines.
When using std::istream and std::ostream (whose std::fstream implements), the recommended usage is to directly use the stream in a bool context instead of calling eof() function because it only return true when you managed to read until the last byte of the file. If there was any error before that, the function will still return true.
So, you should have written your code as:
int countlines() {
ifstream myfile;
int linenumber = 0;
string linecontent;
myfile.open("question.txt", ios::in);
while (getline(myfile, linecontent)) {
if (!linecontent.empty()) {
++linenumber;
}
}
return linenumber;
}
Try the following code. It will also (hopefully) give you an idea why the file open is failing...
int countlines()
{
ifstream myfile;
myfile.open("questions.txt");
string contents;
int linenumber = 0;
if (myfile.is_open())
{
while (getline(myfile, contents))
{
if (contents != "")
linenumber++;
}
cout << "there are " << linenumber << " lines." << endl;
myfile.close();
}
else
cout << "Unable to get file (reason: " << strerror(errno) << ")." << endl;
return linenumber;
}