How to properly read and overwrite the same file in C++ - c++

The question in how to read, modify on-fly and save date to the same std::fstream handled file like this:
std::fstream file(input_name.c_str(), std::ios::out | std::ios::in | std::ios::app | std::ifstream::binary);
std::vector<unsigned char> byte;
inputFile.seekg(0, file.end);
long long int length = file.tellg();
file.seekg(0, file.beg);
lldiv_t divresult = lldiv(length, blockInput);
for (long long int a = 0; a != divresult.quot; a++) {
byte.reserve(blockInput / sizeof(unsigned char));
byte.insert(byte.begin(), blockInput / sizeof(unsigned char), 0);
file.read((char*)&byte[0], blockInput);
for (int size_counter = 0; size_counter != blockInput; size_counter++) {
byte[size_counter] = Transform(rounds, byte[size_counter], bytesUsed++);
}
//the same as lower
file.write((char*)&byte[0], blockInput);
byte.clear();
}
if (divresult.rem > 0) {
byte.reserve(divresult.rem / sizeof(unsigned char));
byte.insert(byte.begin(), divresult.rem / sizeof(unsigned char), 0);
file.read((char*)&byte[0], divresult.rem);
for (int size_counter = 0; size_counter != divresult.rem; size_counter++) {
byte[size_counter] = Transform(rounds, byte[size_counter], bytesUsed++);
}
//what should be here?
//perhaps "file.seekg( file.tellg() - bufferSize );" but I'm not sure what to do next..
file.write((char*)&byte[0], divresult.rem);
byte.clear();
}
file.close();
Do you Guys know how to do it correctly?

Nah, the answer is to modify both read and write stream positions by std::istream::seekg and std::istream::seekp in specific places marked by me as here:
std::fstream file(input.c_str(), std::ios::in | std::ios::out | std::ifstream::binary);
std::vector<unsigned char> byte;
file.seekg(0, file.end);
long long int length = file.tellg();
file.seekg(0, file.beg);
file.seekp(0, file.beg);
lldiv_t divresult = lldiv(length, blockInput);
for (long long int a = 0; a != divresult.quot; a++) {
std::vector<unsigned char> byte;
byte.reserve(blockInput / sizeof(unsigned char));
byte.insert(byte.begin(), blockInput / sizeof(unsigned char), 0);
//HERE
file.seekp(a*blockInput, std::ios_base::beg);
file.read((char*)&byte[0], blockInput);
for (int size_counter = 0; size_counter != blockInput; size_counter++) {
byte[size_counter] = Transform(rounds, byte[size_counter]);
}
//HERE
file.seekg(a*blockInput, std::ios_base::beg);
file.write((char*)&byte[0], blockInput);
byte.clear();
}
if (divresult.rem > 0) {
byte.reserve(divresult.rem / sizeof(unsigned char));
byte.insert(byte.begin(), divresult.rem / sizeof(unsigned char), 0);
//HERE
file.seekp(divresult.quot*blockInput, std::ios_base::beg);
file.read((char*)&byte[0], divresult.rem);
for (int size_counter = 0; size_counter != divresult.rem; size_counter++) {
byte[size_counter] = Transform(rounds, byte[size_counter]);
}
//AAAND HERE
file.seekg(divresult.quot*blockInput, std::ios_base::beg);
file.write((char*)&byte[0], divresult.rem);
byte.clear();
}
file.close();
Seem to be easy but, it was hard to find out what the problem actually is. I hope somebody will be able to use it somewhere :)

Related

Find a number in a binary file, rotate and overwrite

I'm trying to find an integer (6664) in a binary file (file.bin) and I must shift this integer to the right once and write this result to the same position the integer was found. However, I am not able to overwrite the original value with the shifted value. I developed the code below:
#include <cstdio>
#include <cstring>
int main(){
//opening file
FILE *pfile
pfile = fopen("file.bin","r+b");
//ffinding file size
fseek(pfile, 0L, SEEK_END);
long size= ftell(pfile);
rewind(pfile);
//reading data
int vec[size];
fread(vec, sizeof(int), size, pfile);
for(int i = 0; i<size; i++){
if(vec[i]==6664){
int aux = vec[i]>>1;
fseek(pfile, i, SEEK_SET);
fwrite(&aux, sizeof(int), 1, pfile);
}
}
return 0;
}
I generated the file with the following command:
echo "0000000: 6408 0623 77ef bfbd efbf bdef bfbd 2779 d..#w.........'y
0000010: efbf bdef 081a 0000 0000 efbf bdef bfbd ................
0000020: 4577 efbf bdef bfbd efbf bd00 Ew.........." | xxd -r > file.bin
Any thoughts?
IMHO, you should read in chunks. There is no guarantee that your platform or executable has enough memory to read in the file (files can be huge).
static const size_t QUANTITY_INTEGERS = 1024u * 1024u;
uint16_t buffer[QUANTITY_INTEGERS] = {0};
std::ifstream number_file("file.bin", ios::binary);
while (number_file.read((char *) &buffer[0], QUANTITY_INTEGERS * sizeof(uint16_t)))
{
unsigned int numbers_read = number_file.gcount();
uint16_t * p_number = std::find(&buffer[0], &buffer[numbers_read], 0x6664);
if (p_number != &buffer[numbers_read])
{
std::cout << "0x6664 found.\n";
break;
}
}
You can use whatever buffer capacity you want.
Aside from the complete lack of error handling, you are not adequately differentiating between bytes and integers.
Your size variable is expressed in bytes, but you are using it as a count when dealing with your vec array. As such, you are over-allocating your array, reading too many ints from the file into the array, and iterating through too many elements of the array.
Also, when writing back to the file, you are treating the loop counter i as a byte offset, which it is not, thus you are writing to the wrong offset in the file.
Try something more like this instead:
#include <cstdio>
#include <cstdint>
#include <vector>
typedef int32_t myint_t; // or int16_t, if needed...
int main()
{
//opening file
FILE *pfile = fopen("file.bin", "r+b");
if (!pFile) return -1
//finding file size
//TODO: use f/stat() or std::filesystem::file_size() instead
if (fseek(pfile, 0L, SEEK_END) < 0) {
fclose(pFile);
return -1;
}
long size = ftell(pfile);
if (size < 0) {
fclose(pFile);
return -1;
}
rewind(pfile);
//reading data
std::vector<myint_t> vec(size / sizeof(myint_t));
size_t numItems = fread(vec.data(), sizeof(myint_t), vec.size(), pfile);
for(size_t i = 0; i < numItems; ++i) {
if (vec[i] == 6664) {
vec[i] >>= 1;
// TODO: it is more efficient for the filesystem to make
// smaller relative seeks using SEEK_CUR than to make
// larger seeks from the beginning using SEEK_SET...
if (fseek(pfile, i * sizeof(myint_t), SEEK_SET) < 0) {
fclose(pFile);
return -1;
}
if (fwrite(&vec[i], sizeof(myint_t), 1, pfile) != 1) {
fclose(pFile);
return -1;
}
}
}
fclose(pfile);
return 0;
}
That being said, consider using C++-style file I/O instead of C-style file I/O, eg:
#include <iostream>
#include <vector>
#include <cstdint>
using myint_t = int32_t; // or int16_t, if needed...
int main()
{
std::fstream fs;
fs.exceptions(std::fstream::failbit | std::fstream::badbit);
try {
//opening file
fs.open("file.bin", std::ios::in | std::ios::out | std::ios::binary | std::ios::ate);
//finding file size
//TODO: use f/stat() or std::filesystem::file_size() instead
long size = fs.tellg();
fs.seekg(0);
//reading data
std::vector<myint_t> vec(size / sizeof(myint_t));
fs.read(reinterpret_cast<char*>(vec.data()), sizeof(myint_t) * vec.size());
size_t numItems = fs.gcount() / sizeof(myint_t);
for(size_t i = 0; i < numItems; ++i) {
if (vec[i] == 6664) {
vec[i] >>= 1;
// TODO: it is more efficient for the filesystem to make
// smaller relative seeks using std::ios::cur than to make
// larger seeks from the beginning...
fs.seekp(i * sizeof(myint_t));
fs.write(reinterpret_cast<char*>(&vec[i]), sizeof(myint_t));
}
}
}
catch(...) {
return -1;
}
return 0;
}
Though, the following would be a little bit simpler and safer:
#include <iostream>
#include <vector>
#include <filesystem>
#include <cstdint>
using myint_t = int32_t; // or int16_t, if needed...
int main()
{
std::ifstream ifs;
std::ofstream ofs;
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
ofs.exceptions(std::ofstream::failbit | std::ofstream::badbit);
try {
//opening files
ifs.open("file.bin", std::ios::binary);
ofs.open("file.new", std::ios::binary);
//reading data
myint_t value;
bool found = false;
while (ifs.read(reinterpret_cast<char*>(&value), sizeof(value))) {
if (value == 6664) {
value >>= 1;
found = true;
}
ofs.write(reinterpret_cast<char*>(&value), sizeof(value));
}
ifs.close();
ofs.close();
if (found) {
std::filesystem::rename("file.bin", "file.bak");
std::filesystem::rename("file.new", "file.bin");
std::filesystem::remove("file.bak");
}
else {
std::filesystem::remove("file.new");
}
}
catch(...) {
return -1;
}
return 0;
}

Read a binary (exe,zip etc..) file into a char*, c++

I'm trying to read a file and put bytes in a byte buffer, but when i try to read exe or zip file not all bytes are loaded in the buffer. my function:
char* read_file_bytes(const string &name) {
FILE *img = fopen(name.c_str(), "rb");
fseek(img, 0, SEEK_END);
unsigned long filesize = ftell(img);
char *buffer = (char*)malloc(sizeof(char)*filesize);
rewind(img);
fread(buffer, sizeof(char), filesize, img);
return buffer;
}
the piece of code that check the buffer:
char* bytes = read_file_bytes(path);
for(int i = 0; i < strlen(bytes); i++)
cout << hex << (unsigned int)(bytes[i]);
strlen() is designed for text characters, not for binary bytes. It stops counting when it encounters a nul char (0x00), which binary data is likely to contain.
Your read_file_bytes() function knows how many bytes it reads in. You need to return that number to the caller, eg:
typedef unsigned char byte;
byte* read_file_bytes(const std::string &name, unsigned long &filesize)
{
filesize = 0;
FILE *img = fopen(name.c_str(), "rb");
if (!img)
return NULL;
if (fseek(img, 0, SEEK_END) != 0)
{
fclose(img);
return NULL;
}
long size = ftell(img);
if (size == -1L)
{
fclose(img);
return NULL;
}
byte *buffer = static_cast<byte*>(std::malloc(size));
if (!buffer)
{
fclose(img);
return NULL;
}
rewind(img);
if (fread(buffer, 1, size, img) < size)
{
free(buffer);
close(img);
return NULL;
}
fclose(img);
filesize = size;
return buffer;
}
unsigned long filesize;
byte* bytes = read_file_bytes(path, filesize);
for(unsigned long i = 0; i < filesize; ++i)
std::cout << std::hex << static_cast<unsigned int>(bytes[i]);
free(bytes);
Note that this approach is very C-ish and error prone. A more C++ approach would look like this instead:
using byte = unsigned char;
// or, use std::byte in C++17 and later...
std::vector<byte> read_file_bytes(const std::string &name)
{
std::ifstream img;
img.exceptions(std::ifstream::failbit | std::ifstream::badbit);
img.open(name.c_str(), std::ifstream::binary | std::ifstream::ate);
std::ifstream::pos_type size = img.tellg();
ifs.seekg(0, std::ios::beg);
// or, use std::filesystem::file_size() instead...
std::vector<byte> buffer(size);
img.read(buffer.data(), size);
return buffer;
}
std::vector<byte> bytes = read_file_bytes(path);
for(byte b : bytes)
std::cout << std::hex << static_cast<unsigned int>(b);

Copy file in C++ using fstream in binary mode

Below is my function to copy file from source to destination
void CopyFile(const std::string& source, const std::string& destination)
{
std::ifstream ifs(source.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
if (!ifs.good())
return;
std::ofstream ofs(destination.c_str(), std::ios::out | std::ios::binary);
const int bufferSize = 4;
char buffer[bufferSize];
std::streamoff streamSize = ifs.tellg();
std::streamoff iteration = streamSize / bufferSize;
int rem = streamSize % bufferSize;
ifs.seekg(std::ios::beg);
while (iteration > 0)
{
ifs.read(buffer, bufferSize);
ofs.write(buffer, bufferSize);
ifs.seekg(bufferSize, std::ios::cur);
ofs.seekp(bufferSize, std::ios::cur);
--iteration;
}
ifs.read(buffer, rem);
ofs.write(buffer, rem);
ifs.close();
ofs.close();
}
other approach where i open file in non binary mode working for me but not this one. what am i missing here?

Flip and crop a bitmap

I'm getting a bytearray (32 bit or 16 bit) from a source.
If the size width is odd, the last pixel in each row needs to be dropped.
If the height is odd, the last row needs to be dropped.
If the height is negative the bitmap needs to be flipped vertically.
Here is my code so far:
m_pbmiLast = new BITMAPINFO(*m_pbmi);
m_pbmiLast->bmiHeader.biWidth = abs(m_pbmiLast->bmiHeader.biWidth) - (abs(m_pbmiLast->bmiHeader.biWidth) % 2);
m_pbmiLast->bmiHeader.biHeight = abs(m_pbmiLast->bmiHeader.biHeight) - (abs(m_pbmiLast->bmiHeader.biHeight) % 2);
int biWidth = m_pbmiLast->bmiHeader.biWidth;
int biHeight = m_pbmiLast->bmiHeader.biHeight;
int iAdjustedStride = ((((biWidth * m_pbmiLast->bmiHeader.biBitCount) + 31) & ~31) >> 3);
int iRealStride = ((((m_pbmi->bmiHeader.biWidth * m_pbmi->bmiHeader.biBitCount) + 31) & ~31) >> 3);
if (m_pbmi->bmiHeader.biHeight < 0) {
/* Copy the actual data */
int iLineOffsetSource = 0;
int iLineOffsetDest = (biHeight - 1) * iRealStride;
for (int i = 0; i < biHeight; ++i) {
memcpy(&pData[iLineOffsetDest], &m_inputBuffer[iLineOffsetSource], iAdjustedStride);
iLineOffsetSource += iRealStride;
iLineOffsetDest -= iRealStride;
}
} else {
int iLineOffset = 0;
for (int i = 0; i < biHeight; ++i) {
memcpy(&pData[iLineOffset], &m_inputBuffer[iLineOffset], iAdjustedStride);
iLineOffset += iRealStride;
}
}
It doesn't flip the bitmap, and when the bitmap is an odd width, it slants the bitmap.
Can be done like so.. I include the reading and writing just to make it an SSCCE. It has little to no error.
As for my comment about new BITMAPINFO. I was saying that you don't have to allocate such a small structure on the HEAP. Ditch the new part. The only allocation you need for a bitmap is the pixels. The header and other info does not need an allocation at all.
See the Flip function below.
#include <iostream>
#include <fstream>
#include <cstring>
#include <windows.h>
typedef struct
{
BITMAPFILEHEADER Header;
BITMAPINFO Info;
unsigned char* Pixels;
} BITMAPDATA;
void LoadBmp(const char* path, BITMAPDATA* Data)
{
std::ifstream hFile(path, std::ios::in | std::ios::binary);
if(hFile.is_open())
{
hFile.read((char*)&Data->Header, sizeof(Data->Header));
hFile.read((char*)&Data->Info, sizeof(Data->Info));
hFile.seekg(Data->Header.bfOffBits, std::ios::beg);
Data->Pixels = new unsigned char[Data->Info.bmiHeader.biSizeImage];
hFile.read((char*)Data->Pixels, Data->Info.bmiHeader.biSizeImage);
hFile.close();
}
}
void SaveBmp(const char* path, BITMAPDATA* Data)
{
std::ofstream hFile(path, std::ios::out | std::ios::binary);
if (hFile.is_open())
{
hFile.write((char*)&Data->Header, sizeof(Data->Header));
hFile.write((char*)&Data->Info, sizeof(Data->Info));
hFile.seekp(Data->Header.bfOffBits, std::ios::beg);
hFile.write((char*)Data->Pixels, Data->Info.bmiHeader.biSizeImage);
hFile.close();
}
}
void Flip(BITMAPDATA* Data)
{
unsigned short bpp = Data->Info.bmiHeader.biBitCount;
unsigned int width = std::abs(Data->Info.bmiHeader.biWidth);
unsigned int height = std::abs(Data->Info.bmiHeader.biHeight);
unsigned char* out = new unsigned char[Data->Info.bmiHeader.biSizeImage];
unsigned long chunk = (bpp > 24 ? width * 4 : width * 3 + width % 4);
unsigned char* dst = out;
unsigned char* src = Data->Pixels + chunk * (height - 1);
while(src != Data->Pixels)
{
std::memcpy(dst, src, chunk);
dst += chunk;
src -= chunk;
}
std::memcpy(dst, src, chunk); //for 24-bit.
std::swap(Data->Pixels, out);
delete[] out;
}
int main()
{
BITMAPDATA Data;
LoadBmp("C:/Users/Brandon/Desktop/Bar.bmp", &Data);
Flip(&Data);
SaveBmp("C:/Users/Brandon/Desktop/Foo.bmp", &Data);
delete[] Data.Pixels;
return 0;
}

c++ socket binary file

i have this function to get the content of file ,
#define BUFSIZE 512
vector<int> getContFile(char* pFile) {
ifstream vCin(pFile, ios::binary);
ifstream::pos_type size;
// get vLength of file:
vCin.seekg(0, ios::end);
size = vCin.tellg();
vCin.seekg(0, ios::beg);
vector<int> vTmp;
for (int i = 0; i < size; i++)
vTmp.push_back(vCin.get());
vCin.close();
return vTmp;
}
and this to send to the server
void SendFile() {
SendS("upFileUser");
int i;
vector<int> vTmp = getContFile("/usr/home/alex/Desktop/eval.tar");
for (i = 0; i < vTmp.size(); i += BUFSIZE) {
char *vBuff = new char[BUFSIZE];
for (int j = i; j < BUFSIZE; j++)
vBuff[j] = (char(vTmp[i]));
SendS(vBuff);
}
if (i < (vTmp.size() - 1)) {
char *vBuff = new char[vTmp.size() - i];
for (int j = 0; j < vTmp.size() - i; j++)
vBuff[j + i] = (char(vTmp[j + i]));
SendS(vBuff);
}
sendS("endOfFileTransmision");
}
void SendS(char* pSir) {
int vLen = strlen(pSir);
write(pSocket, &vLen, sizeof (int));
write(pSocket, pSir, vLen);
}
this is the receve function
char* reciveS() {
char* vTmp;
int vCt = 0;
read(pSocket, &vCt, sizeof (vCt));
if (vCt != 0) {
vTmp = new char[vCt];
read(vSocket, vTmp, vCt);
} else {
vTmp = NULL;
}
return vTmp;
}
bool receveFile(void) {
char* vReceve = reciveS();
if (strcmp(vReceve, "upFileUser") == 0)
{
ofstream vCoutFile;
vCoutFile.open("data2.tar", ios::out | ios::binary);
while (true) {
char *vTmp = new char[BUFSIZ];
vTmp = reciveS();
cout<<vTmp;
if (strcmp(vTmp, "endOfFileTransmision") == 0) break;
else {
cout << vTmp;
vCoutFile << vTmp;
}
}
vCoutFile.close();
}
}
and the result are a broke pipe(i run this to freebsd 6.4 amd with g++ compiler) , so what i miss , the connection are good i can transfer text from client to server and reverse the problem are with binary file
I see two problems with your code:
You are making a lot of allocations (new) but you never free the memory.
In the SendS function you are taking the string length, but the data in that "string" is from a vector of integers and is binary. This means that the data can contain the string-terminating '\0' character (the integer 0).
Besides that, I really don't follow what you are doing. Instead of reading into a vector, create a char-buffer and allocate enough memory to put the whole file into that buffer (char *buffer = new char[length_of_file]) and send it, with the length of the buffer first.
Something like this:
std::pair<size_t, char *> getContFile(const char *pFile)
{
ifstream vCin(pFile, ios::binary);
ifstream::pos_type size;
vCin.seekg(0, ios::end);
size = vCin.tellg();
vCin.seekg(0, ios::beg);
char *buffer = new char[size];
vCin.read(buffer, size);
return std::make_pair(static_cast<size_t>(size), buffer);
}
void SendFile()
{
SendS("upFileUser", strlen("upFileUser"));
std::pair<size_t, char *> vTmp = getContFile("/usr/home/alex/Desktop/eval.tar");
SendS(vTmp.second, vTmp.first);
delete [] vTmp.second;
}
void SendS(char *buffer, size_t length)
{
// Send the length
size_t tmp = htonl(length);
write(pSocket, &tmp, sizeof(tmp));
// Send the buffer
while (length > 0)
{
ssize_t sent = write(pSocket, buffer, length);
if (sent <= 0)
{
// Some kind of error
break;
}
buffer += sent;
length -= sent;
}
}
Do something similar on the receiving side.