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.
Related
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.
Hey I've a dynamic array and I want to load to this array the data of my Wav file, I already wrote the beginning but I can't figure it out how to load the file in my dynamic array, can somebody help me further with this code?
#include <iostream>
using namespace std;
template <typename T>
class Array{
public:
int size;
T *arr;
Array(int s){
size = s;
arr = new T[size];
}
T& operator[](int index)
{
if (index > size)
resize(index);
return arr[index];
}
void resize(int newSize) {
T* newArray = new T[newSize];
for (int i = 0; i <size; i++)
{
newArrayi] = arr[i];
}
delete[] arr;
arr = newArray;
size = newSize;
}
};
int main(){
Array<char> wavArray(10);
FILE *inputFile;
inputFile =fopen("song.wav", "rb");
return 0;
}
if you just want to load the complete file into memory, this may come in handy:
#include <iterator>
// a function to load everything from an istream into a std::vector<char>
std::vector<char> load_from_stream(std::istream& is) {
return {std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>()};
}
... and use the C++ file streaming classes to open and automatically close files.
{
// open the file
std::ifstream is(file, std::ios::binary);
// check if it's opened
if(is) {
// call the function to load all from the stream
auto content = load_from_stream(is);
// print what we got (works on textfiles)
std::copy(content.begin(), content.end(),
std::ostream_iterator<char>(std::cout));
} else {
std::cerr << "failed opening " << file << "\n";
}
}
... but a WAV file contains a lot of different chunks describing the contents of the file so you may want to create individual classes for streaming these chunks to and from files.
char* readFileBytes(const char *name)
{
FILE *fl = fopen(name, "r");
fseek(fl, 0, SEEK_END);
long len = ftell(fl);
char *ret = malloc(len);
fseek(fl, 0, SEEK_SET);
fread(ret, 1, len, fl);
fclose(fl);
return ret;
}
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...
When I try to read a file to a buffer, it always appends random characters to the end of the buffer.
char* thefile;
std::streampos size;
std::fstream file(_file, std::ios::in | std::ios::ate);
if (file.is_open())
{
size = file.tellg();
std::cout << "size: " << size;
thefile = new char[size]{0};
file.seekg(0, std::ios::beg);
file.read(thefile, size);
std::cout << thefile;
}
int x = 0;
While my original text in my file is: "hello"
The output becomes: "helloýýýý««««««««þîþîþ"
Could anyone help me as to what is happening here? Thanks
From the C++ docs: http://cplusplus.com/reference/istream/istream/read
"This function simply copies a block of data, without checking its contents nor appending a null character at the end."
So your string misses the trailing null character which indicates the end of the string. In this case cout will just continue printing characters from what is beyond thefile in memory.
Add a '\0' at the end of your string.
If the file is not opened with ios::binary mode, you cannot assume that the position returned by tellg() will give you the number of chars that you will read. Text mode operation may perform some transformations on the flow (f.ex: on windows, it will convert "\r\n" in the file in "\n", so you might find out a size of 2 but read only 1)
Anyway, read() doesn't add a null terminator.
Finally, you must allocate one more character than the size that you expect due to the null terminator that you have to add. Otherwise you risk a buffer overflow when you add it.
You should verify how many chars were really read with gcount(), and set a null terminator to your string accordingly.
thefile = new char[size + 1]{0}; // one more for the trailing null
file.seekg(0, std::ios::beg);
if (file.read(thefile, size))
thefile[size]=0; // successfull read: all size chars were read
else thefile[file.gcount()]=0; // or less chars were read due to text mode
Here's a better way of reading your collection:
#include <vector>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cstdint>
#include <iterator>
template<class T>
void Write(std::string const & path, T const & value, std::ios_base::openmode mode)
{
if (auto stream = std::ofstream(path, mode))
{
Write(stream, value);
stream.close();
}
else
{
throw std::runtime_error("failed to create/open stream");
}
}
template<class T>
void Write(std::ostream & stream, T const & value)
{
std::copy(value.begin(), value.end(), std::ostreambuf_iterator<char>(stream));
if (!stream)
{
throw std::runtime_error("failed to write");
}
}
template<class T>
void Read(std::istream & stream, T & output)
{
auto eof = std::istreambuf_iterator<char>();
output = T(std::istreambuf_iterator<char>(stream), eof);
if(!stream)
{
throw std::runtime_error("failed to read stream");
}
}
template<class T>
void Read(std::string const & path, T & output)
{
if (auto stream = std::ifstream(path, std::ios::in | std::ios::binary))
{
Read(stream, output);
stream.close();
}
else
{
throw std::runtime_error("failed to create stream");
}
}
int main(void)
{
// Write and read back text.
{
auto const s = std::string("I'm going to write this string to a file");
Write("temp.txt", s, std::ios_base::trunc | std::ios_base::out);
auto t = std::string();
Read("temp.txt", t);
}
// Write and read back a set of ints.
{
auto const v1 = std::vector<int>() = { 10, 20, 30, 40, 50 };
Write("temp.txt", v1, std::ios_base::trunc | std::ios_base::out | std::ios_base::binary);
auto v2 = std::vector<int>();
Read("temp.txt", v2);
}
return 0;
}
Pass in an iterable container rather than using "new".
I'm attempting to create a custom std::streambuf which acts as a sub-stream to a parent stream. This is an adaptation of the implementation outlined in this SO thread answer.
In this example below I am attempting to simply read the first 5 characters "Hello," of the stream. However, when I call ifstream.read() into the buffer, the buffer is filled with "ello, ", as though it is off by one.
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
stringstream ss;
ss << "Hello, World!";
substreambuf asd(ss.rdbuf(), 0, 5);
istream istream(&asd);
char buffer[6] = { '\0' };
istream.read(buffer, sizeof(buffer));
cout << buffer << endl; //prints "ello, "
}
I am new to streambufs and I feel like I'm missing something obvious here. Any help would be appreciated.
Here is the definition for substreambuf:
#include <iostream>
#include <sstream>
namespace std
{
struct substreambuf : public streambuf
{
explicit substreambuf(streambuf* sbuf, streampos pos, streampos len) :
m_sbuf(sbuf),
m_pos(pos),
m_len(len),
m_read(0)
{
m_sbuf->pubseekpos(pos);
setbuf(nullptr, 0);
}
protected:
int underflow()
{
if (m_read >= m_len)
{
return traits_type::eof();
}
return m_sbuf->sgetc();
}
int uflow()
{
if (m_read >= m_len)
{
return traits_type::eof();
}
m_read += 1;
return m_sbuf->snextc();
}
streampos seekoff(streamoff off, ios_base::seekdir seekdir, ios_base::openmode openmode = ios_base::in | ios_base::out)
{
if (seekdir == ios_base::beg)
{
off += m_pos;
}
else if (seekdir == ios_base::cur)
{
off += m_pos + m_read;
}
else if (seekdir == ios_base::end)
{
off += m_pos + m_len;
}
return m_sbuf->pubseekpos(off, openmode) - m_pos;
}
streampos seekpos(streampos streampos, ios_base::openmode openmode = ios_base::in | ios_base::out)
{
streampos += m_pos;
if (streampos > m_pos + m_len)
{
return -1;
}
return m_sbuf->pubseekpos(streampos, openmode) - m_pos;
}
private:
streambuf* m_sbuf;
streampos m_pos;
streamsize m_len;
streampos m_read;
};
};
I thought this was strange when I first saw the code.
int uflow()
{
// ...
return m_sbuf->snextc();
}
Why is he returning the result of snextc()? The policy defined for uflow() is "return the next available character and advance the input stream by one character". If snextc() is called, the input sequence will be advanced, and then it will return the next character. The result is that at least 1 character is skipped.
The correct method to call would be sbumpc() because it will cache the next character first, advance the input stream, and then return it.
return m_sbuf->sbumpc();
Here is a demo.