So I'm making basic CRUD
create work fine
but when the code reach file.read(code)
VS display Read Access Violation
When I try to run each line 1 by 1 in read function there's no error until I reach file.read
I'm not able to figure out the causes
I suspect the problem is in here:
Mahasiswa read(fstream &file, int pos) {
Mahasiswa result;
file.open("data.bin", ios::in | ios::binary);
file.seekp(pos * sizeof(Mahasiswa));
file.read(reinterpret_cast<char*>(&result), sizeof(Mahasiswa));
file.close();
return result;
}
This is the entire code:
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
struct Mahasiswa {
int no;
int pk;
string nim;
string nama;
string jurusan;
};
void create(fstream &file, int no, int pk, string nim, string nama, string jurusan) {
file.open("data.bin", ios::app | ios::out | ios::binary);
Mahasiswa mhs;
mhs.no = no;
mhs.pk = pk;
mhs.nim = nim;
mhs.nama = nama;
mhs.jurusan = jurusan;
file.write(reinterpret_cast<char*>(&mhs), sizeof(Mahasiswa));
file.close();
}
Mahasiswa read(fstream &file, int pos) {
Mahasiswa result;
file.open("data.bin", ios::in | ios::binary);
file.seekp(pos * sizeof(Mahasiswa));
file.read(reinterpret_cast<char*>(&result), sizeof(Mahasiswa));
file.close();
return result;
}
int main()
{
fstream file;
create(file, 1, 12, "0123", "Person", "TIK");
Mahasiswa til = read(file, 0);
cout << til.nama;
}
source code from the YouTuber:
Apparently this code works:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Mahasiswa {
int NIM;
string nama;
string jurusan;
};
Mahasiswa ambilData(int posisi, fstream& myFile) {
Mahasiswa bufferData;
myFile.seekp((posisi - 1) * sizeof(Mahasiswa));
myFile.read(reinterpret_cast<char*>(&bufferData), sizeof(Mahasiswa));
return bufferData;
}
void menulisData(Mahasiswa& data, fstream& myFile) {
myFile.write(reinterpret_cast<char*>(&data), sizeof(Mahasiswa));
}
void menulisDataByPos(int posisi, Mahasiswa& bufferData, fstream& myFile) {
myFile.seekg((posisi - 1) * sizeof(Mahasiswa));
myFile.write(reinterpret_cast<char*>(&bufferData), sizeof(Mahasiswa));
}
int main() {
fstream myFile;
myFile.open("data.bin", ios::trunc | ios::out | ios::in | ios::binary);
Mahasiswa mahasiswa1, mahasiswa2, mahasiswa3, output;
mahasiswa1.NIM = 123;
mahasiswa1.nama = "ucup";
mahasiswa1.jurusan = "memasak";
mahasiswa2.NIM = 124;
mahasiswa2.nama = "otong";
mahasiswa2.jurusan = "menjahit";
mahasiswa3.NIM = 125;
mahasiswa3.nama = "sandra";
mahasiswa3.jurusan = "mesin";
menulisData(mahasiswa1, myFile);
menulisData(mahasiswa2, myFile);
menulisData(mahasiswa3, myFile);
mahasiswa2.nama = "mario";
menulisDataByPos(2, mahasiswa2, myFile);
output = ambilData(2, myFile);
cout << output.NIM << endl;
cout << output.nama << endl;
cout << output.jurusan << endl;
myFile.close();
cin.get();
return 0;
}
From language lawyer's point of view an UB happens here:
file.write(reinterpret_cast<char*>(&mhs), sizeof(Mahasiswa));
and here:
file.read(reinterpret_cast<char*>(&result), sizeof(Mahasiswa));
What happens here is an equivalent of memcpy. memcpy is an equivalent of shallow copy, but can be used only on so-called "POD" types, which are:
a) trivially constructible - no user-defined default constructor;
b) trivially copyable - no user-defined copy constructor or operator;
c) standard memory layout - no virtual inheritance, virtual members, const non-static members, etc.
std::string isn't a aggregate and isn't a POD, it stores a pointer to separate memory storage where actually string data resides, so it cannot be shallow copied and got special members defined to manage that. As those are members, i.e. subobjects of Mahasiswa, Mahasiswa cannot be copied that way either. Rare case when std::string can be memcpy-ed is when it uses short string optimization, which is hard to predict in portable way.
I finally found an answer to this question.
I just need to close and reopen the stream
Here is the explanation why I do this
https://stackoverflow.com/a/32056770/14882773
though it still has an Access violation error at least it has output as expected
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct pelajar {
string NIM;
int num;
};
int main()
{
fstream data;
pelajar lopzx = {"123",1};
data.open("data.bin", ios::binary | ios::in | ios::out);
pelajar man;
data.write(reinterpret_cast<char*>(&lopzx), sizeof(pelajar));
data.close(); //close data
data.open("data.bin", ios::binary | ios::in | ios::out); //re-open data
data.read(reinterpret_cast<char*>(&man), sizeof(pelajar)); //then read
std::cout << man.NIM << man.num;
data.close();
}
But if we change struct NIM to a char array, close, then reopen there will be no error anymore
pelajar {
char NIM[50];
int num;
};
and give it a value like this:
pelajar lopzx = {"123",1};
Note :
Somehow outside of lopzx curly braces
lopzx.NIM = "val"; doesn't work.
and there are probably cons to this method that I'm not aware of.
Related
I do not understand why my array of pointers is only saving the last line from the file that I am reading from. When I substitute a string literal into the setData() function the code works just fine. All that the "mann" file contains are a bunch of words order alphabetically. Thank you.
#include <iostream>
#include <fstream>
using namespace std;
class orignialData {
char* data;
public:
void setData(char* s) { data = s;}
char* getData() const {return data;}
};
class dataClass {
orignialData** W_;
public:
dataClass(char* filename);
void addData();
void viewAll();
};
dataClass::dataClass(char* filename) {
fstream file;
file.open(filename, ios::in);
if (file.fail()) {
cout << "There was an error reading the file...\n";
}
W_ = 0;
W_ = new orignialData*[5];
for (int i = 0; i < 5; i++)
W_[i] = new orignialData;
char buff[30];
char* temp;
while(file >> buff) {
cout << buff << endl;
static int i = 0;
W_[i] -> setData(buff);
i++;
}
file.close();
}
Instead of data = s, write data = strdup(s) to make a copy of the contents. Otherwise, you will assign the same pointer again and again, and you will overwrite the contents of the memory to which this pointer points again and again. At the end, your temporary buffer will contain the last line of your file, and all the pointers will point to exactly this buffer. That's what you are observing...
I am confused as to where in my code am I getting it wrong. Instead of resulting to 1,2,3 the output is 1,1,1. Any suggestions as to what I can do? I am guessing my error lies either in the writing data or when I use the variable value.
class Binary
{
public:
Binary(int num);
~Binary();
void createBinary();
void writeBinary();
void readBinary();
string binFile;
int value;
fstream binaryFile;
};
Binary::Binary(int num)
{
value = num;
binFile = "BinaryFile.bin";
}
Binary::~Binary()
{
}
void Binary::createBinary()
{
binaryFile.open(binFile, ios::out | ios::binary);
binaryFile.close();
}
void Binary::writeBinary()
{
if (!binaryFile) //if file does not exist
{
createBinary();
}
binaryFile.open(binFile, ios::app | ios::binary);
binaryFile.write((char*)&value, sizeof(value));
binaryFile.close();
}
void Binary::readBinary()
{
binaryFile.open(binFile, ios::in |ios::binary);
binaryFile.read((char*)&value, sizeof(value));
binaryFile.close();
cout << value << ", ";
}
int main()
{
Binary num1(1);
Binary num2(2);
Binary num3(3);
num1.writeBinary();
num2.writeBinary();
num3.writeBinary();
num1.readBinary();
num2.readBinary();
num3.readBinary();
return 0;
}
Since you use a single file and read from start at each Binary()::readBinary(), your result is as expected. Either use different files for each of your objects.
// In general we may need some random part in name but for this special case,
// since you are just writing / reading value, the following seems to work.
Binary::Binary(int num)
{
value = num;
binFile = "BinaryFile"; // common start
binFile += std::to_string(value); // differentiator
binFile += ".bin"; // extension
}
Or, if you want to use the same file, try to get help from a static variable for not to read the same line again and again.
Apologize for my poor English.
I am stuck by fstream in C++. Here is my code.
#include<iostream>
#include<fstream>
using namespace std;
struct TestStruct
{
int a;
char str[30];
TestStruct(int a_, const char* s)
{
a = a_;
strcpy_s(str,sizeof(char)*30, s);
}
TestStruct() = default;
};
int main()
{
fstream output("out.bin", ios::out|ios::binary);
output.seekp(0,ios::end);
cout << output.tellp() << endl;
for (int i = 0; i < 15; i++) {
TestStruct a(10*i, "asdadas");
output.write(reinterpret_cast<char*>(&a), sizeof(a));
}
output.close();
fstream input("out.bin", ios::in | ios::binary);
input.seekg(2 * sizeof(TestStruct), ios::beg);
for (int i = 0; i < 5; i++) {
TestStruct a;
input.read(reinterpret_cast<char*>(&a), sizeof(a));
cout <<"file_pointer"<<input.tellg()<<'\t'<<a.a << endl;
}
}
I use seekp(0,ios::end) to add new entry in the file. So the file should get lager when I run this code. But actually the file haven't change at all.
Here is the output:
> 0 <--tellp() always return 0
> file_pointer108 20
> file_pointer144 30
> file_pointer180 40
> file_pointer216 50
> file_pointer252 60
Add ios::app to the output's flags. You won't need to do output.seekp(0, ios::end); then.
While it may not seem like it seekp(0, ios::end) is actually working.
The reason it returns 0 is because you accidentally create a new empty file.
And the end cursor position of a new empty file is 0.
It creates a new file because of the file mode you use:
output("out.bin", ios::out|ios::binary);
https://en.cppreference.com/w/cpp/io/basic_filebuf/open
I have a method which writes a binary file from an int array. (it could be wrong too)
void bcdEncoder::writeBinaryFile(unsigned int packedBcdArray[], int size)
{
fstream binaryIo;
binaryIo.open("PridePrejudice.bin", ios::out| ios::binary | ios::trunc);
binaryIo.seekp(0);
binaryIo.write((char*)packedBcdArray, size * sizeof(packedBcdArray[0]));
binaryIo.seekp(0);
binaryIo.close();
}
I need to now read that binary file back. And preferably have it read it back into another array of unsigned ints without any information loss.
I have something like the following code, but I have no idea on how reading binary files really works, and no idea how to read it into an array of ints.
void bcdEncoder::readBinaryFile(string fileName)
{
// myArray = my dnynamic int array
fstream binaryIo;
binaryIo.open(fileName, ios::in | ios::binary | ios::trunc);
binaryIo.seekp(0);
binaryIo.seekg(0);
binaryIo.read((int*)myArray, size * sizeof(myFile));
binaryIo.close();
}
Question:
How to complete the implementation of the function that reads binary files?
If you're using C++, use the nice std library.
vector<unsigned int> bcdEncoder::readBinaryFile(string fileName)
{
vector<unsigned int> ret; //std::list may be preferable for large files
ifstream in{ fileName };
unsigned int current;
while (in.good()) {
in >> current;
ret.emplace_back(current);
}
return ret;
}
Writing is just as simple (for this we'll accept an int[] but an std library would be preferable):
void bcdEncoder::writeBinaryFile(string fileName, unsigned int arr[], size_t len)
{
ofstream f { fileName };
for (size_t i = 0; i < len; i++)
f << arr[i];
}
Here's the same thing but with an std::vector
void bcdEncoder::writeBinaryFile(string fileName, vector<unsigned int> arr)
{
ofstream f { fileName };
for (auto&& i : arr)
f << i;
}
To simplify read operation consider storing size (i.e the number of elements in the array) before the data:
void bcdEncoder::writeBinaryFile(unsigned int packedBcdArray[], int size)
{
fstream binaryIo;
binaryIo.open("PridePrejudice.bin", ios::out| ios::binary | ios::trunc);
binaryIo.seekp(0);
binaryIo.write(&size, sizeof(size));
binaryIo.write((char*)packedBcdArray, size * sizeof(packedBcdArray[0]));
binaryIo.close();
}
The read would look something like:
void bcdEncoder::readBinaryFile(string fileName)
{
std::vector<unsigned int> myData;
int size;
fstream binaryIo;
binaryIo.open(fileName, ios::in | ios::binary | ios::trunc);
binaryIo.read(&size, sizeof(size)); // read the number of elements
myData.resize(size); // allocate memory for an array
binaryIo.read(myData.data(), size * sizeof(myData.value_type));
binaryIo.close();
// todo: do something with myData
}
Modern alternative using std::array
Here's a code snippet that uses more modern C++ to read a binary file into an std::array.
const int arraySize = 9216; // Hard-coded
std::array<uint8_t, arraySize> fileArray;
std::ifstream binaryFile("<my-binary-file>", std::ios::in | std::ios::binary);
if (binaryFile.is_open()) {
binaryFile.read(reinterpret_cast<char*>(fileArray.data()), arraySize);
}
Because you're using an std::array you'll need to know the exact size of the file during compile-time. If you don't know the size of the file ahead of time (or rather, you'll need to know that the file has at least X bytes available), use a std::vector and look at this example here: https://stackoverflow.com/a/36661779/1576548
Thanks for the tips guys, looks like I worked it out!! A major part of my problem was that half the arguments and syntax I added to the methods were not required, and actually messed things up. Here are my working methods.
void bcdEncoder::writeBinaryFile(unsigned int packedBcdArray[], int size, string fileName)
{
ofstream binaryIo;
binaryIo.open(fileName.substr(0, fileName.length() - 4) + ".bin", ios::binary);
if (binaryIo.is_open()) {
binaryIo.write((char*)packedBcdArray, size * sizeof(packedBcdArray[0]));
binaryIo.close();
// Send binary file to reader
readBinaryFile(fileName.substr(0, fileName.length() - 4) + ".bin", size);
}
else
cout << "Error writing bin file..." << endl;
}
And the read:
void bcdEncoder::readBinaryFile(string fileName, int size)
{
AllocateArray packedData(size);
unsigned int *packedArray = packedData.createIntArray();
ifstream binaryIo;
binaryIo.open(fileName, ios::binary);
if (binaryIo.is_open()) {
binaryIo.read((char*)packedArray, size * sizeof(packedArray[0]));
binaryIo.close();
decodeBCD(packedArray, size * 5, fileName);
}
else
cout << "Error reading bin file..." << endl;
}
With the AllocateArray being my class that creates dynamic arrays without vectors somewhat safely with destructors included.
I've made this class to read binary files and store their data.
FileInput.h:
#pragma once
#include <Windows.h>
#include <fstream>
using namespace std;
class FileInput
{
public:
FileInput(LPSTR Filename);
FileInput(LPWSTR Filename);
~FileInput();
operator char*();
explicit operator bool();
size_t Size;
private:
__forceinline void Read();
ifstream File;
char* Data;
};
FileInput.cpp
#include "FileInput.h"
FileInput::FileInput(LPSTR Filename)
{
File.open(Filename, ios::binary);
Read();
}
FileInput::FileInput(LPWSTR Filename)
{
File.open(Filename, ios::binary);
Read();
}
FileInput::~FileInput()
{
if (Data) delete[] Data;
}
FileInput::operator char*()
{
return Data;
}
FileInput::operator bool()
{
return (bool)Data;
}
void FileInput::Read()
{
if (!File)
{
Data = nullptr, Size = 0;
return;
}
File.seekg(0, ios::end);
Size = (size_t)File.tellg();
File.seekg(0, ios::beg);
Data = new char[Size];
File.read(Data, Size);
File.close();
}
Then I use it like this:
FileInput File(Filename); // This reads the file and allocates memory
if (!File) // This is for error checking
{
// Do something
}
if (File.Size >= sizeof(SomeType))
{
char FirstChar = File[0]; // Gets a single character
SomeStruct *pSomeStruct = reinterpret_cast<SomeStruct*>(&File[0]); // Gets a structure
}
So, is there any possibility that this class may be unsafe?
A reinterpret_cast<SomeStruct*>(&File) or other nonsense statement doesn't count.
EDIT: What I mean with unsafe is "to do unexpected or 'dangerous' things".
This is major overkill. If you want to copy a file into a buffer, you can use use an istreambuf_iterator:
std::ifstream inFile(fileName);
std::vector<char> fileBuffer ( (std::istreambuf_iterator<char>(inFile)),
std::istreambuf_iterator<char>() );
Then you can read from fileBuffer as necessary.