Fstream not reading a complete struct from binary data (C++) - c++

I've been trying to make my program write a string into a binary file using Ofstream::write(), but I could not find out how to (through the interwebs), so I tried writing a struct with a string into the file. That worked perfectly; I could open the file and read the string (with my human eyes), but when I tried to use Ifstream::read() to read the struct, I just got an empty string and the string that I wrote (in this case, "dir" was the empty one, and "fileName" was correctly read).
Any and all help is appreciated :)
PS: Both strings are saved in the file...
This is my writing code:
StringStruct texPath;
texPath.dir = "src/Assets/";
texPath.fileName = "bricks_top.png";
file.write((char*)&texPath, sizeof(texPath));
This is my reading code:
StringStruct texFile;
file.read((char*)&texFile, sizeof(texFile));
std::string filepath = "";
filepath += texFile.dir;
filepath += texFile.fileName;
std::cout << filepath;
And this is the "StringStruct" code:
struct StringStruct {
std::string dir = "src/Assets/";
std::string fileName = "Example.png";
};

Ok, I recieved some comments (thanks manni66) saying that I have to write as c-strings. So I changed my struct to this:
struct StringStruct {
char* dir = "src/Assets/";
char* fileName = "Example.png";
};
So that I was writing each string as a c-string instead.

Related

How to read UTF-8 file data in C++?

I have a list of IPA (UTF-8) symbols in a text file called ipa.txt with numbers assigned to them. How do I cross reference it with a source file which is also a text file that contains a bunch of words and their corresponding IPA, to return a text file for every names with their names as their filename and inside the text file should contain their corresponding numbers of IPA.
Below is what I've tried but didn't work, only outputs were mostly 000000.
int main()
{
std::unordered_map <wchar_t, int> map;
std::wifstream file;
file.open("ipa.txt");
if (file.is_open()) {
std::cout << "opened ipa file";
}
wchar_t from;
int to;
while (file >> from >> to) {
map.insert(std::make_pair(from, to));
}
std::wifstream outfile;
outfile.open("source.txt");
if (outfile.is_open()) {
std::cout << "opened source file";
}
std::wstring id;
std::wstring name;
while (outfile >> id >> name) {
std::ofstream outputfile;
outputfile.open(id + L".txt");
for (wchar_t c : name) outputfile << map[c];
}
system("pause");
return 0;
}
I believe you are using the wrong type for c used in the iteration over name. As c is used as key for the map, and name is a wstring, you should use:
for (wchar_t c : name) outputfile << map[c];
instead of:
for (char c : name) outputfile << map[c];
Isn't it?
Hope this may help, Stefano
First thought:
map <- std::unordered_map<char, int>
open ipa.txt:
for each line in file:
map[line[0]] = line[1]
open source.txt:
for each line in file:
create and open line[0].txt:
for each char in line[1]:
write map[char] to line[0].txt
Regarding the actual C++ implementation, AFAIK utf-8 should fit inside char and std::string so you don't have to do anything special there. If you need utf-8 string literals you must use the u8 prefix: u8"literal". Everything else should be standard file IO.
EDIT: Here are some links to the relevant documentation to help you get started:
ifstream (for reading from files)
ofstream (for writing to files)
unordered_map (for mapping 'keys' to 'values')
Outside of that it will probably just take a little Googling. File IO is very common so I'm sure you can find some good examples online. As long as your file format is consistent you shouldn't have too much trouble with the file parsing. Then the rest of it is just storing values in the map and then looking them up when you need them, which is pretty simple.

c++ ofstream write_to_log.open (" relative path + array");

I want the line below to write a new file using the content given in the array
but into a new folder named logs:
char log_file_name[100]; /* this array contains the name of a new file */
ofstream write_to_log;
write_to_log.open (relative path, log_file_name , fstream::app);
How do I get it working ?
You can use CreateDirectory for creating folders with VC++ in Windows.
#include <windows.h>
#include <string>
#include <fstream>
using namespace std;
int main() {
string path = "C:\\users\\folder";
CreateDirectory(path.c_str(), NULL);
char log_file_name[100] = "log.txt";
path += '\\';
path += log_file_name;
ofstream write_to_log(path.c_str(), fstream::app);
return 0;
}
The NULL refers to a security attributes structure that you may have to create. More details at MSDN here and in this answer.
You can save your self a lot of potential trouble and replace char log_file_name[100]; with std::string log_file_name; The benefits of string are many, the most important here are they resize and they make appending really easy. The string does everything a char array does and a whole lot of extras. In virtually all cases, you should chose a string over a char array.
string path;
string log_file_name;
With the path and the file name as strings
path += "\\" + log_file_name
ofstream write_to_log(path, fstream::app);
if (write_to_log)
{ // file is open and looks writable (have to start writing to be sure)
// do stuff. Or not. It's a free country.
}
else
{ // file didn't open
// Handle error
}
All done and the file, if it exists and is writable, is open and ready to go. Always check the state of a stream when you use it. SO is littered with questions from people who didn't and got confused by the result.
On older compilers you may have to change the create and open line slightly:
ofstream write_to_log(path.c_str(), fstream::app);

Name *.bin file after string

I'm trying to get a string from the user through stdin and save it to the variable InputString, then create a binary file with the same name as the value of InputString. This is the code I've written so far:
std::string InputString;
getline(std::cin, InputString);
std::cout << InputString << std::endl;
// The code above works.
// Errors start below. :(
void Printi(std::string filename)
{
std::ofstream Printi(filename".bin");
Printi((char*)&Hans, sizeof(Person)); // Hans is an instance of my class Person.
Printi.close();
}
Printi(InputString);
I get the following errors (translated into English from my localized compiler):
"Printi": Local function definition is not allowed
Missing ")" (in line std::ofstream Printi..)
How can I solve this problem using only standard C++ libraries?
std::ofstream Printi(filename".bin") needs to be std::ofstream Printi(filename + ".bin"). The + operator is used to concatenate the strings and append the .bin to the end of what was supplied in the file name.

Adding to the name of a file

I'm working on an application that processes text files, and I want to create a new file with a similar name to that file, but slightly modified.
So for instance, I have a function that takes a string fileName as a parameter and creates a new file with the word "PROCESSED" added before ".txt."
so if fileName = "testFile.txt"
the new file should be named "testFilePROCESSED.txt"
string newFile = filename + "PROCESSED"; obviously doesn't work since the filename would be "testFile.txtPROCESSED" in this case.
You just need more practice with strings:
int ii = filename.rfind('.');
filename.insert(ii, "PROCESSED");
Let's keep it simple, I assume fileName is a string.
`#include <sstream>`
using namespace std;
stringstream ss;
fileName.erase(name.end()-4, name.end()); //Extension removal.
ss << name << "PROCESSED.txt";
string newFileName = ss.str();

Reading a string from a file in C++

I'm trying to store strings directly into a file to be read later in C++ (basically for the full scope I'm trying to store an object array with string variables in a file, and those string variables will be read through something like object[0].string). However, everytime I try to read the string variables the system gives me a jumbled up error. The following codes are a basic part of what I'm trying.
#include <iostream>
#include <fstream>
using namespace std;
/*
//this is run first to create the file and store the string
int main(){
string reed;
reed = "sees";
ofstream ofs("filrsee.txt", ios::out|ios::binary);
ofs.write(reinterpret_cast<char*>(&reed), sizeof(reed));
ofs.close();
}*/
//this is run after that to open the file and read the string
int main(){
string ghhh;
ifstream ifs("filrsee.txt", ios::in|ios::binary);
ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
cout<<ghhh;
ifs.close();
return 0;
}
The second part is where things go haywire when I try to read it.
Sorry if it's been asked before, I've taken a look around for similar questions but most of them are a bit different from what I'm trying to do or I don't really understand what they're trying to do (still quite new to this).
What am I doing wrong?
You are reading from a file and trying to put the data in the string structure itself, overwriting it, which is plain wrong.
As it can be verified at http://www.cplusplus.com/reference/iostream/istream/read/ , the types you used were wrong, and you know it because you had to force the std::string into a char * using a reinterpret_cast.
C++ Hint: using a reinterpret_cast in C++ is (almost) always a sign you did something wrong.
Why is it so complicated to read a file?
A long time ago, reading a file was easy. In some Basic-like language, you used the function LOAD, and voilĂ !, you had your file.
So why can't we do it now?
Because you don't know what's in a file.
It could be a string.
It could be a serialized array of structs with raw data dumped from memory.
It could even be a live stream, that is, a file which is appended continuously (a log file, the stdin, whatever).
You could want to read the data word by word
... or line by line...
Or the file is so large it doesn't fit in a string, so you want to read it by parts.
etc..
The more generic solution is to read the file (thus, in C++, a fstream), byte per byte using the function get (see http://www.cplusplus.com/reference/iostream/istream/get/), and do yourself the operation to transform it into the type you expect, and stopping at EOF.
The std::isteam interface have all the functions you need to read the file in different ways (see http://www.cplusplus.com/reference/iostream/istream/), and even then, there is an additional non-member function for the std::string to read a file until a delimiter is found (usually "\n", but it could be anything, see http://www.cplusplus.com/reference/string/getline/)
But I want a "load" function for a std::string!!!
Ok, I get it.
We assume that what you put in the file is the content of a std::string, but keeping it compatible with a C-style string, that is, the \0 character marks the end of the string (if not, we would need to load the file until reaching the EOF).
And we assume you want the whole file content fully loaded once the function loadFile returns.
So, here's the loadFile function:
#include <iostream>
#include <fstream>
#include <string>
bool loadFile(const std::string & p_name, std::string & p_content)
{
// We create the file object, saying I want to read it
std::fstream file(p_name.c_str(), std::fstream::in) ;
// We verify if the file was successfully opened
if(file.is_open())
{
// We use the standard getline function to read the file into
// a std::string, stoping only at "\0"
std::getline(file, p_content, '\0') ;
// We return the success of the operation
return ! file.bad() ;
}
// The file was not successfully opened, so returning false
return false ;
}
If you are using a C++11 enabled compiler, you can add this overloaded function, which will cost you nothing (while in C++03, baring optimizations, it could have cost you a temporary object):
std::string loadFile(const std::string & p_name)
{
std::string content ;
loadFile(p_name, content) ;
return content ;
}
Now, for completeness' sake, I wrote the corresponding saveFile function:
bool saveFile(const std::string & p_name, const std::string & p_content)
{
std::fstream file(p_name.c_str(), std::fstream::out) ;
if(file.is_open())
{
file.write(p_content.c_str(), p_content.length()) ;
return ! file.bad() ;
}
return false ;
}
And here, the "main" I used to test those functions:
int main()
{
const std::string name(".//myFile.txt") ;
const std::string content("AAA BBB CCC\nDDD EEE FFF\n\n") ;
{
const bool success = saveFile(name, content) ;
std::cout << "saveFile(\"" << name << "\", \"" << content << "\")\n\n"
<< "result is: " << success << "\n" ;
}
{
std::string myContent ;
const bool success = loadFile(name, myContent) ;
std::cout << "loadFile(\"" << name << "\", \"" << content << "\")\n\n"
<< "result is: " << success << "\n"
<< "content is: [" << myContent << "]\n"
<< "content ok is: " << (myContent == content)<< "\n" ;
}
}
More?
If you want to do more than that, then you will need to explore the C++ IOStreams library API, at http://www.cplusplus.com/reference/iostream/
You can't use std::istream::read() to read into a std::string object. What you could do is to determine the size of the file, create a string of suitable size, and read the data into the string's character array:
std::string str;
std::ifstream file("whatever");
std::string::size_type size = determine_size_of(file);
str.resize(size);
file.read(&str[0], size);
The tricky bit is determining the size the string should have. Given that the character sequence may get translated while reading, e.g., because line end sequences are transformed, this pretty much amounts to reading the string in the general case. Thus, I would recommend against doing it this way. Instead, I would read the string using something like this:
std::string str;
std::ifstream file("whatever");
if (std::getline(file, str, '\0')) {
...
}
This works OK for text strings and is about as fast as it gets on most systems. If the file can contain null characters, e.g., because it contains binary data, this doesn't quite work. If this is the case, I'd use an intermediate std::ostringstream:
std::ostringstream out;
std::ifstream file("whatever");
out << file.rdbuf();
std::string str = out.str();
A string object is not a mere char array, the line
ifs.read(reinterpret_cast<char*>(&ghhh), sizeof(ghhh));
is probably the root of your problems.
try applying the following changes:
char[BUFF_LEN] ghhh;
....
ifs.read(ghhh, BUFF_LEN);