1I am currently working on a project of creating my own game in OpenGL. My problem is right now, that if I read a file, that my function reading that file results in a SIGABRT, because of something inside the std::ifstream deconstructor (more specifically in "std::basic_ifstream<char, std::char_traits<char> >::~basic_ifstream()"). This function previously worked for me, but suddenly stopped working.
My Goal is simple: A reliable implementation for reading a file to a char*. Multi threading is currently not my concern.
Here is my implementation of the file reading function.
It takes in a path, and should write the content of the file at that path into the out parameter.
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
#include <cassert>
#include "Utility.h"
char * Utility::readFile(const char* path,char*& out){
#ifndef NDEBUG
std::cout<<"Getting file: "<<path<<"\n";
#endif
// Open the file, but freak out if not valid.
std::ifstream file=std::ifstream(path);
assert(file.good());
if(!file.good())
{
throw std::runtime_error((std::string)"Couldn't open file for loading: "+path);
}
// Read the file contents into a char buffer.
std::stringstream buffer;buffer << file.rdbuf();
std::string fileContentsStr = buffer.str();
out = new char[fileContentsStr.size()];
strcpy(out,fileContentsStr.c_str());
return out;
}
My code is located at C0D3-M4513R/OpenGlGame.
I already tried a minimal example, which is working and using the same compile flags (except linker flags). test.txt and test1.txt just contain some rubbish text generated by randomly hacking on my keyboard.
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
//This Function is the same as the one above!!!
char *readFile(const char *path, char *&out) {
#ifndef NDEBUG
std::cout << "Getting file: " << path << "\n";
#endif
// Open the file, but freak out if not valid.
std::ifstream file = std::ifstream(path);
assert(file.good());
if (!file.good()) {
throw std::runtime_error((std::string) "Couldn't open file for loading: " + path);
}
// Read the file contents into a char buffer.
std::stringstream buffer;
buffer << file.rdbuf();
//convert the stringstream to a string
std::string fileContentsStr = buffer.str();
//copy the contents of the string to a char array
out = new char[fileContentsStr.size()];
strcpy(out, fileContentsStr.c_str());
//return char array address (which should be the same as the start?)
return out;
}
int main() {
//The programm started!
std::cout << "Hello, World!" << std::endl;
//Define a space for the contents of the file to live
char *out;
//Read the contents of a file
out = readFile("test.txt", out);
//Print contents of the file
std::cout << out << std::endl;
char *out1;
//Read the contents of a file
out1 = readFile("test1.txt", out1);
//Print contents of the file
std::cout << out1 << std::endl;
return 0;
}
strcpy:
Copies the character string pointed to by src, including the null terminator, to the character array whose first element is pointed to by dest.
The behavior is undefined if the dest array is not large enough. The behavior is undefined if the strings overlap.
c_str:
Returns a pointer to a null-terminated character array with data equivalent to those stored in the string.
out = new char[fileContentsStr.size()];
strcpy(out,fileContentsStr.c_str());
You need to be careful when mixing std::string with c-strings, because a std::string is not null-terminated and does not count the nullterminator for its size. However, c_str does return a pointer to a null-terminated character array.
You are asking strcpy to write fileContentsStr.size()+1 (size + null terminator) into a char array with only fileContentsStr.size() elements.
PS: As mentioned in a comment, you should consider to return a std::string instead. You are using a raw owning pointer which is error prone and should be avoided. Either use a smart-pointer or let a std::string manage the char-array (thats what its made for actually ;).
Related
In this function what I have to do is pass the strings from txt file in char and do some operations. My only problem is on pass file from txt to char. how i should fix it?
char* foo(string& input){
stringstream ss;
ss<<input;
char *elements=new char[32];
elements[32]='\0';
ss>>elements; //next part code not written because useless
This is how you can store data in a char array from a file:
Source File
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inFile;
inFile.open("Input File.txt");
char Array[50] = { ' ' };
inFile.get(Array, 50);
cout << "Output : " << Array << endl;
inFile.close();
}
Text File
Enter Text Here
If you want i can make a dynamic array for you which will have the exact size as the string (Data you input) from file or use vectors because they can easily be expanded and shortened in late binding(means: during program execution).
This question already has answers here:
Read file line by line using ifstream in C++
(8 answers)
Closed 3 years ago.
I cant write a words from a file to an array.
I have tried to use char and strings, but i have problem with both of them.
FILE *file = fopen("films.txt", "r");
string FILMS[500];
while (!feof(file))
{
fscanf(file, "%s", FILMS);
//fgets(FILMS, 500, file);
}
I expect that in each cell there will be a word.
Use the C++ classes and functions to make it easier. Instead of a fixed C style array of exactly 500 films, use a std::vector<std::string>> that will grow dynamically when you put film titles in it.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> get_films() {
std::ifstream file("films.txt");
std::vector<std::string> FILMS;
if(file) { // check that the file was opened ok
std::string line;
// read until getline returns file in a failed/eof state
while(std::getline(file, line)) {
// move line into the FILMS vector
FILMS.emplace_back(std::move(line));
// make sure line is in a specified state again
line.clear();
}
}
return FILMS;
} // an fstream is automatically closed when it goes out of scope
int main() {
auto FILMS = get_films();
std::cout << "Read " << FILMS.size() << " film titles\n";
for(const std::string& film : FILMS) {
std::cout << film << "\n";
}
}
As I'm not sure why you tried using c style arrays and files, I posted a 'not too elegant' solution like that one, too, hoping it might help. You could always try to make it more dynamic with some malloc (or new), but I sticked with the easy solution for now.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
void readcpp(const char* fname, std::vector<std::string>& data)
{
std::ifstream file_in(fname, std::ios::in);
if (file_in.is_open())
{
std::string film;
while (std::getline(file_in, film))
{
data.push_back(film);
}
file_in.close();
}
else std::cerr << "file cant be opened" << std::endl;
}
#include <cstdio>
#include <cstdlib>
#include <cstring>
void readc(const char* fname, char data[500][500])
{
FILE* file_in = fopen(fname, "r");
if (file_in)
{
char film[500];
for (unsigned int i = 0; fgets(film, 500, file_in) && i < 500; i++)
{
memcpy(data + i, film, 500);
}
fclose(file_in);
}
else fprintf(stderr, "file cant be opened\n");
}
int main()
{
const char* fname = "films.txt";
char cFilms[500][500];
std::vector<std::string> cppFilms;
readc(fname, cFilms);
readcpp(fname, cppFilms);
return 0;
}
And as the others mentioned before, do not use feof or for that matter, ifstream's eof member function either, for checking wheter you reached the end of file, as it may be unsafe.
Hm, I see a lot of code in answers.
The usage of algorithm will drastically reduce coding effort.
Additionally it is a "more modern" C++ approach.
The OP said, that he want to have words in some array. OK.
So we will use a std::vector<std::string> for storing those words. As you can see in cppreference, the std::vector has many different constructors. We will use number 4, the range constructor.
This will construct the vector with a range of similar data. The similar data in our case are words or std::string. And we would like to read the complete range of the file, beginning with the first word and ending with the last word in the file.
For iterating over ranges, we use iterators. And for iterating of data in files, we use the std::istream_iterator. We tell this function what we want to read as template parameter, in our case a std::string. Then we tell it, from which file to read.
Since we do not have files on SO, I use a std::istringstream. But that's the same reading from a std::ifstream. If you have na open file stream, then you can hand it over to the std::istream_iterator.
And the result of using this C++ algorithms is that we read the complete file into the vector by just defining the varaible with its constructer as a one-liner.
We do similar for the debug output.
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <sstream>
std::istringstream filmFile{ R"(Film1 Film2
Film3 Film4 Film5
Film6
)" };
int main()
{
// Define the variable films and use its range constructor
std::vector<std::string> films{ std::istream_iterator<std::string>(filmFile), std::istream_iterator<std::string>() };
// For debug pruposes, show result on console
std::copy(films.begin(), films.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
Sorry for the long headline. I couldn't know how to describe it in short words.
Would you care to recreate the problem i am going through?
You can use any wav file to read.
I am trying to query the chunks in a wav file here, this is the simplified version of the code, but i think it might be enough to recreate if there is a problem.
I use a mac, and compile with g++ -std=c++11.
When i run this code and don't include the line std::cout << query << std::endl; then std::find(chunk_types.begin(), chunk_types.end(), query) != chunk_types.end() returns 0 in all iterations. But i know the binary file contains some of these chunks. If i include the line then it works properly, but that is also not predictable lets say it works properly sometimes.
I am a bit perplexed am i doing anything wrong here?
#include <fstream>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
int main(){
std::vector<std::string> chunk_types{
"RIFF","WAVE","JUNK","fmt ","data","bext",
"cue ","LIST","minf","elm1",
"slnt","fact","plst","labl","note",
"adtl","ltxt","file"};
std::streampos fileSize;
std::ifstream file(/* file path here */, std::ios::binary);
file.seekg(0, std::ios::beg);
char fileData[4];
for(int i{0};i<100;i+=4){ //100 is an arbitrary number
file.seekg(i);
file.read((char*) &fileData[0], 4);
std::string query(fileData);
std::cout << query << std::endl;
/* if i put this std::cout here, it works or else std::find always returns 0 */
if( std::find(chunk_types.begin(), chunk_types.end(), query) != chunk_types.end() ){
std::cout << "found " + query << std::endl;
}
}
return 0;
}
std::string query(fileData) uses strlen on fileData to find its terminating 0, but doesn't find one because fileData is not zero-terminated and continues searching for 0 up the stack until it finds it or hits inaccessible memory past the end of the stack and causes SIGSEGV.
Also file.read can read fewer symbols than expected, gcount must be used to extract the actual number of characters last read:
A fix:
file.read(fileData, sizeof fileData);
auto len = file.gcount();
std::string query(fileData, len);
A slightly more efficient solution is to read directly into std::string and keep reusing it to avoid a memory allocation (if no short string optimisation) and copying:
std::string query;
// ...
constexpr int LENGTH = 4;
query.resize(LENGTH);
file.read(&query[0], LENGTH);
query.resize(file.gcount());
This snippet is part of a big program. The problem I am facing is that when I write a string to the file using "write" member function, it do not show last character of string:
#include <iostream>
#include <cstring>
#include <string>
#include <cctype>
#include <fstream>
#include <cstring>
using namespace std;
int main()
{
fstream file1("/users/xxxxxxx/desktop/file1.txt", ios::out);
string data;
cout << "Enter string: " << endl;
getline(cin, data);
file1.write(reinterpret_cast<char*>(&data), data.size());
//file1 << data;
file1.close();
return 0;
}
For Example: If Input String: "Hello World".
On File it will show: "Hello Worl",
But it does work fine if I input string using "file1 << data". Please help me in this
file1.write(reinterpret_cast<char*>(&data), data.size());
Don't do this, you are writing the string object itself to the file. if you really want to use write you have to get a pointer to the first char that the string holds, like this:
file1.write(data.data(), data.size());
Just use the << operator.
Why would you cast the address of a string into a char*? This isn't a meaningful conversion--you are casting a std::basic_string<char>* to char*. I suspect you want to treat string as char* since write accepts const char* as parameter. You can access the character sequence stored in your data by doing data.c_str().
Is there anyway I can transfer data from an fstream (a file) to a stringstream (a stream in the memory)?
Currently, I'm using a buffer, but this requires double the memory, because you need to copy the data to a buffer, then copy the buffer to the stringstream, and until you delete the buffer, the data is duplicated in the memory.
std::fstream fWrite(fName,std::ios::binary | std::ios::in | std::ios::out);
fWrite.seekg(0,std::ios::end); //Seek to the end
int fLen = fWrite.tellg(); //Get length of file
fWrite.seekg(0,std::ios::beg); //Seek back to beginning
char* fileBuffer = new char[fLen];
fWrite.read(fileBuffer,fLen);
Write(fileBuffer,fLen); //This writes the buffer to the stringstream
delete fileBuffer;`
Does anyone know how I can write a whole file to a stringstream without using an inbetween buffer?
ifstream f(fName);
stringstream s;
if (f) {
s << f.rdbuf();
f.close();
}
// need to include <algorithm> and <iterator>, and of course <fstream> and <sstream>
ifstream fin("input.txt");
ostringstream sout;
copy(istreambuf_iterator<char>(fin),
istreambuf_iterator<char>(),
ostreambuf_iterator<char>(sout));
In the documentation for ostream, there are several overloads for operator<<. One of them takes a streambuf* and reads all of the streambuffer's contents.
Here is a sample use (compiled and tested):
#include <exception>
#include <iostream>
#include <fstream>
#include <sstream>
int main ( int, char ** )
try
{
// Will hold file contents.
std::stringstream contents;
// Open the file for the shortest time possible.
{ std::ifstream file("/path/to/file", std::ios::binary);
// Make sure we have something to read.
if ( !file.is_open() ) {
throw (std::exception("Could not open file."));
}
// Copy contents "as efficiently as possible".
contents << file.rdbuf();
}
// Do something "useful" with the file contents.
std::cout << contents.rdbuf();
}
catch ( const std::exception& error )
{
std::cerr << error.what() << std::endl;
return (EXIT_FAILURE);
}
The only way using the C++ standard library is to use a ostrstream instead of stringstream.
You can construct a ostrstream object with your own char buffer, and it will take ownership of the buffer then (so no more copying is needed).
Note however, that the strstream header is deprecated (though its still part of C++03, and most likely, it will always be available on most standard library implementations), and you will get into big troubles if you forget to null-terminate the data supplied to the ostrstream.This also applies to the stream operators, e.g: ostrstreamobject << some_data << std::ends; (std::ends nullterminates the data).
If you're using Poco, this is simply:
#include <Poco/StreamCopier.h>
ifstream ifs(filename);
string output;
Poco::StreamCopier::copyToString(ifs, output);