Reading and Writing with char* to file - c++

I am wondering how can you write data of type char*,int,double using char* and also reading a whole file line by line using again char* ? I know it can be done with std:string really beautiful but I am interested with char*. I have created a Write() method which writes char* successfully
but I don't know how to adjust it for ints and doubles, also I have started creating Read() method to read each line and save it to char* then print it to the console but I don't know how to implement it.
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
void Write(char* fileName, char* pData)
{
ofstream file (fileName, ios::out | ios::app );
if (file.is_open())
{
size_t len = strlen(pData);
file.write(pData, len);
}
}
void Read(char* fileName, char* pData)
{
ifstream file(fileName, ios::in );
if(file.is_open())
{
file.read((char*)&pData, sizeof(pData));
file.close();
}
}
int main()
{
char* fileName = "E:\\cpp\\CarsIO\\data.txt";
char* data = "hello\n";
Write(fileName , data);
char* read = "";
Read(fileName , read);
return 0;
}

I have created a Write() method which writes char* successfully but I don't know how to adjust it for ints and doubles [...]
First of all, the function std::ofstream::write is intended for unformatted (binary) I/O. Since you are outputting text, it would be easier to use the formatted I/O functions, for example operator <<, like this:
void Write( char* fileName, char* pData )
{
ofstream file( fileName, ios::out | ios::app );
if ( file.is_open() )
{
file << pData;
}
if ( !file )
{
//TODO: handle error
}
}
In order for the function Write to print data of type int, you can simply create an appropriate overloaded function Write, like this:
void Write( char* fileName, int data )
{
ofstream file( fileName, ios::out | ios::app );
if ( file.is_open() )
{
file << data;
}
if ( !file )
{
//TODO: handle error
}
}
In order to make it also print the data type double, you can create an additional overloaded function for this data type. You can simply copy the function above and change int data to double data.
However, now you have 3 overloaded functions, one for the data type char *, one for int and one for double. That is a lot of unnecessary code duplication. It would be less messy to make a single template function, which can handle all three data types:
template <typename T>
void Write( char* fileName, T data )
{
ofstream file( fileName, ios::out | ios::app );
if ( file.is_open() )
{
file << data;
}
if ( !file )
{
//TODO: handle error
}
}
[...] also I have started creating Read() method to read each line and save it to char* then print it to the console but I don't know how to implement it.
The function std::ifstream::read is intended for unformatted (binary) input, not for formatted text input. Since you insist on using char* instead of std::string, I recommend that you use the function std::istream::getline in order to read exactly one line of text input, like this:
void Read( char* fileName, char* pData, std::streamsize count )
{
ifstream file(fileName, ios::in );
if(file.is_open())
{
file.getline( pData, count );
}
if ( !file )
{
//TODO: handle error
}
}
In your code, you were simply using sizeof(pData) to pass the size of the memory buffer. This will not work, because this will give you the size of the pointer pData (which is probably 4 or 8 bytes), instead of the size of the memory buffer. That is why the function Read must take an additional parameter which specifies the size of the memory buffer.
In your code, you are calling the function Read like this:
char* read = "";
Read(fileName , read);
This code is wrong, for two reasons:
You must ensure that the memory buffer is large enough to store the read data. This would be handled automatically when using std::string, but you must handle this yourself when using char*.
The line char* read = ""; makes the pointer read point to an (empty) string literal. String literals are read only. That is why C++ requires that pointers to string literals are declared as const char * instead of char *. You cannot pass a pointer to a read-only string literal to the function Read, because that function will attempt to write to that string literal, which causes undefined behavior.
In order to call the function Read and print the result, you can use the following code:
char buffer[100];
Read( fileName, buffer, sizeof buffer );
std::cout << buffer << "\n";

Related

read from file with read() in c++

hi i need to do something like filesystem and i need to write and read from file (the write function work) i have a function signature
void read(int addr, int size, char *ans);
void BlockDeviceSimulator::read(int addr, int size, char *ans) {
memcpy(ans, filemap + addr, size);
}
and this is my function to read from file and print it
std::string MyFs::get_content(std::string path_str) {
std::string ans;
//open file
BlockDeviceSimulator *newFile = new BlockDeviceSimulator(path_str);
newFile->read(1,newFile->DEVICE_SIZE,(char*)&ans);
std::cout << ans << std::endl;
delete newFile;
return "";
}
can you help me what is wrong here and why it dosen't print?
You are trying to cast address of std::string object to pointer to char. At first you need to allocate enough size to read into std::string - ans.resize(newFile->DEVICE_SIZE);. Secondly you need to get char * from std::string - &ans[0].

C++ From text file to Binary file

I've a task to copy elements from .txt file[direct access file] to .bin file[fixed length record file] (homework).
.txt file holds strings. Every line has one word.
I came up with code below, but I'm not sure if that's what is needed and even slighly correct. Any help will be useful! (I'm new to C++)
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
const int buffer_size = 30;
class Word{
char name[buffer_size];
public:
void setName () // Trying to get every word from a line
{
string STRING;
ifstream infile;
infile.open ("text.txt");
while(!infile.eof()) // To get you all the lines.
{
getline(infile,STRING); // Saves the line in STRING.
}
infile.close();
}
};
void write_record()
{
ofstream outFile;
outFile.open("binFILE.bin", ios::binary | ios::app);
Word obj;
obj.setName();
outFile.write((char*)&obj, sizeof(obj));
outFile.close();
}
int main()
{
write_record();
return 0;
}
NEW APPROACH:
class Word
{
char name[buffer_size];
public:
Word(string = "");
void setName ( string );
string getName() const;
};
void readWriteToFile(){
// Read .txt file content and write into .bin file
string temp;
Word object;
ofstream outFile("out.dat", ios::binary);
fstream fin ("text.txt", ios::in);
getline(fin, temp);
while(fin)
{
object.setName(temp);
outFile.write( reinterpret_cast< const char* >( &object ),sizeof(Word) );
getline(fin, temp);
}
fin.close();
outFile.close();
}
int main()
{
readWriteToFile();
return 0;
}
Word::Word(string nameValue)
{
setName(nameValue);
}
void Word::setName( string nameString )
{
// Max 30 char copy
const char *nameValue = nameString.data();
int len = strlen(nameValue);
len = ( len < 31 ? len : 30);
strncpy(name, nameValue, len);
name[len] = '\0';
}
string Word::getName() const
{
return name;
}
Quick commentary and walk through
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
Avoid using namespace std; while you are learning. It can lead to some really nasty, hard to pin-down bugs as your functions may be silently replaced by functions with the same name in the standard library.
const int buffer_size = 30;
class Word
{
char name[buffer_size];
Since it looks like you are allowed to use std::string why not use it here?
public:
void setName() // Trying to get every word from a line
Really bad name for a function that apparently is supposed to // Trying to get every word from a line
{
string STRING;
ifstream infile;
infile.open("text.txt");
while (!infile.eof()) // To get you all the lines.
{
getline(infile, STRING); // Saves the line in STRING.
}
Few things wrong here. One is the epic Why is iostream::eof inside a loop condition considered wrong?
Next is while the code reads each line, it doesn't do anything with the line. STRING is never stored anywhere.
Finally in a class that sounds as though it should contain and manage a single word, it reads all the words in the file. There may be a case for turning this function into a static factory that churns out a std::vector of Words.
infile.close();
}
};
void write_record()
{
ofstream outFile;
outFile.open("binFILE.bin", ios::binary | ios::app);
ios::app will add onto an existing file. This doesn't sound like what was described in the assignment description.
Word obj;
obj.setName();
We've already coverred the failings of the Word class.
outFile.write((char*) &obj, sizeof(obj));
Squirting an object into a stream without defining a data protocol or using any serialization is dangerous. It makes the file non-portable. You will find that some classes, vector and string prominent among these, do not contain their data. Writing a string to a file may get you nothing more than a count and an address that is almost certainly not valid when the file is loaded.
In this case all the object contains is an array of characters and that should write to file cleanly, but it will always write exactly 30 bytes and that may not be what you want.
outFile.close();
}
int main()
{
write_record();
return 0;
}
Since this is homework I'm not writing this sucker for you, but here are a few suggestions:
Read file line by line will get you started on the file reader. Your case is simpler because there is only one word on each line. Your teacher may throw a curveball and add more stuff onto a line, so you may want to test for that.
Read the words from the file into a std::vector. vector will make your job so easy that you might have time for other homework.
A very simplistic implementation is:
std::vector<std::string> words;
while (getline(infile, STRING)) // To get you all the lines.
{
words.push_back(STRING);
}
For writing the file back out in binary, I suggest going Pascal style. First write the length of the string in binary. Use a known, fixed width unsigned integer (no such thing as a negative string) and watch out for endian. Once the length is written, write only the number of characters you need to write.
Ignoring endian, you should have something like this:
uint32_t length = word.length(); // length will always be 32 bits
out.write((char*)&length, sizeof(length));
out.write(word.c_str(), length);
When you are done writing the writer, write a reader function so that you can test that the writer works correctly. Always test your code, and I recommend not writing anything until you know how you'll test it. Very often coming at a program from the test side first will find problems before they even have a chance to start.

Writing data structure with char* and std::list to binary files

I am trying to write a data structure as such:
struct dataEntry
{
std::list<int> listTiles;
char* pData;
int nSize;
}
to a binary file.
I used ofstream to write to a binary file:
Write(char* fileName, const dataEntry& dataStruct)
{
ofstream binFile("fileName, ios::out | ios::binary | ios::trunc);
if(binFile.open())
{
binFile.write((char*)&dataStruct, sizeof(dataStruct));
binFile.close();
}
}
I used the same method to read back the binary file:
Read(char* fileName, const dataEntry& dataStruct)
{
ifstream binFile("fileName, ios::in| ios::binary );
if(binFile.open())
{
binFile.read((char*)&dataStruct, sizeof(dataStruct));
binFile.close();
}
}
However, i cannot iterate through the list after i read the binary file. It gave me an exception saying that the "list iterator outside range".
2nd problem is that when i tried to read the binary file the 2nd time, the "pData" is not what I have entered.
int Main()
{
char* name = "C:\\file.dat";
char* buf = "ABCDEFG";
dataEntry newData;
newData.listTiles.push_back(1);
newData.listTiles.push_back(2);
newData.nSize = 5;
newData.pData = buf;
Write(name, newData);
Read(name, newData);
buf = newData.pData; // wrong value when read 2nd time
newData.listTiles.remove(2); // crashed here
}
Check advanced c++ course on Udemy, the instructor said you cannot store pointers to binary files, because when you try to read them back the pointers (addresses) you used before writing them will not still be reserved for the list. So you have to save the data not the pointers

Read whole std::ifstream to heap

I cannot work out why this isn't working. From what I can tell, it doesn't appear to be reading the whole image file... Though I cannot tell. I basically have some raw image that I'd like to read onto the heap.
unsigned char* ReadImageFromFile(const char* FILENAME, unsigned int SIZE_BYTES)
{
unsigned char *data = (unsigned char*) malloc(SIZE_BYTES);
std::ifstream image(FILENAME);
image.read((char*) data, SIZE_BYTES);
image.close();
return data;
}
1) open the file in binary mode
2) don't return a raw pointer that needs to be freed
std::string readImageFromFile(const char* filename)
{
std::ifstream image(filename, std::ios::binary);
std::ostringstream data;
data << image.rdbuf();
return data.str();
}
Or if your prefer to write error-prone code (seems to be popular with the embedded crowd) you could do it this way:
char* readImageFromFile(const char* filename)
{
std::ifstream image(filename, std::ios::binary);
std::ostrstream data;
data << image.rdbuf();
data.freeze();
return data.str();
}
Of course there's a good reason strstreams are deprecated.
Try std::ifstream image(FILENAME, std::ios_base::binary); (note the second argument to ifstream constructor).

How to edit a row in a text file with C++? [duplicate]

This question already has answers here:
Edit a specific row in a file
(5 answers)
Closed 6 years ago.
I have a txt file like this:
"shoes":12
"pants":33
"jacket":26
"glasses":16
"t-shirt":182
I need to replace the number of jacket ( from 26 to 42 for example ). So, I have wrote this code, but I don't know how to edit a specific row where there is the word "jacket":
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ifstream f("file.txt");
string s;
if(!f) {
cout< <"file does not exist!";
return -1;
}
while(f.good())
{
getline(f, s);
// if there is the "jacket" in this row, then replace 26 with 42.
}
f.close();
return 0;
}
In order to modify data in a text file, you'll generally have to read
the entire file into memory, make the modifications there, then rewrite
it. In this case, I'd suggest defining a structure for the entries,
with name and quantity entries, equality defined as equality of the
names, and an overloaded operator>> and operator<< to read and write
it from the file. You're overall logic would then use functions like:
void
readData( std::string const& filename, std::vector<Entry>& dest )
{
std::ifstream in( filename.c_str() );
if ( !in.is_open() ) {
// Error handling...
}
dest.insert( dest.end(),
std::istream_iterator<Entry>( in ),
std::istream_iterator<Entry>() );
}
void
writeData( std::string const& filename, std::vector<Entry> const& data )
{
std::ifstream out( (filename + ".bak").c_str() );
if ( !out.is_open() ) {
// Error handling...
}
std::copy( data.begin(), data.end(), std::ostream_iterator<Entry>( out ) );
out.close();
if (! out ) {
// Error handling...
}
unlink( filename.c_str() );
rename( (filename + ".bak").c_str(), filename.c_str() );
}
(I'd suggest raising exceptions in the error handling, so that you don't
have to worry about the else branches of the ifs. Except for the
creation of the ifstream in the first, the error conditions are exceptional.)
First of all, this is not possible in the naive way. Let's say you want to edit said row but write a larger number, there won't be any space in the file. So usually eidts in the middle are done by rewriting the file or writing a copy. Programs may use memory, temp files, etc and hide this from a user, but chaning some bytes in the middle of a file will only work in very restircted environments.
So what you'll want to do is write another file.
...
string line;
string repl = "jacket";
int newNumber = 42;
getline(f, line)
if (line.find(repl) != string::npos)
{
osstringstream os;
os << repl << ':' << newNumber;
line = os.str();
}
// write line to the new file. For exmaple by using an fstream.
...
If the file has to be the same, you can read all lines to memory, if there is enough memory, or use a temp file for either input or output.