Zapisywanie i odczytywanie Stringów w pliku binarnym

0

Witam, Oto mój kod:

class Save
{
public:
	Save();
	~Save();
	int number1;
	int number2;
};

std::ofstream ofs("Saves/save.bin", std::ios::binary);
Save* file = new Save;
file->number1 = 0;
file->number2 = 1;
ofs.write((char*)(file), sizeof(Save));
delete file;
ofs.close();

std::ifstream ifs("Saves/save.bin", std::ios::binary);
char* temp = new char[sizeof(Save)];
ifs.read(temp, sizeof(Save));
Save* file2 = (Save*)(temp);
av.setAvatarLook(file2->number1);
av.setAvatarLook(file2->number2);
delete file2;
ifs.close();

Wszystko pięknie działa, ale problem się pojawia gdy chcę dodać do klasy Save pole:

sf::String name;
 

lub

std::string name;
 

Sam zapis jeszcze działa, ale gdy mam też odczyt to wywala błędy (Access violation writing location). Muszę zapisać imię gracza. Czy da w ogolę radę? Myślałem też żeby zapisać imię gracza jako tablica char[], ale tu też jest problem bo kompletnie nie wiem jak to zrobić :/ Proszę o pomoc.

0

Zapisz do pliku string.c_str()

I nie alokuj ręcznie pamięci, nie masz ku temu powodów.

2

A ja od siebie dodam, że to co robisz prędzej czy później się zemści. Kod jest przenośny, ale generowany plik binarny już nie. Plik stworzony na systemie 32-bitowym będzie miał (przeważnie) 8 bajtów bo int ma tam (przeważnie) 4 bajty. Przeniesiesz go na system 64-bitowy i masz lipę bo tam int ma (przeważnie) 8 bajtów.

Bezpieczniej będzie użyć typów o stałej długości np. int32_t/int64_t

0

Edit: Udało mi się jednak na tablicy char[] , w sensie że konwertuję string na char, a po wczytaniu w drugą stronę.

2

Pomyślałem nad tym trochę, poprzyglądałem się problemowi i postanowiłem coś spłodzić. Oto efekt:
http://melpon.org/wandbox/permlink/fK4YU1mc4tw7CSNZ

binary_write.hpp

#pragma once

#include <type_traits>
#include <boost/fusion/tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <sstream>
#include <cstring>

namespace detail{
    template<typename OutStream, typename T>
    typename std::enable_if<std::is_fundamental<T>::value>::type write(OutStream &stream, const T &value){
        stream.write(reinterpret_cast<const char *>(&value), sizeof(value));
    }
    
    template<typename OutStream, typename T>
    typename std::enable_if<std::is_same<T,  const char *>::value>::type 
    write(OutStream &stream, const T &str){
        std::size_t len = std::strlen(str)+1;
        write(stream, len);
        stream.write(reinterpret_cast<const char *>(str), len);
    }
    
    template<typename OutStream, typename T>
    typename std::enable_if<std::is_class<T>::value>::type
    write(OutStream &stream, const T &obj){
        obj.write_to(stream);
    }
    
    template<class... Types>
    struct write_proxy{
        write_proxy(Types... args): args(args...){}
        
        template<typename OutStream>
        void to(OutStream &stream){
            for_each(args, [&](auto value){
                write(stream, value);
            });
        }
        
        boost::fusion::tuple<Types...> args;
    };
}

template<class... Types>
auto write(Types... args){
    return detail::write_proxy<Types...>(args...);
}

binary_read.hpp

#pragma once

#include <type_traits>
#include <boost/fusion/tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <sstream>
#include <cstring>
#include <string>
#include <memory>

namespace detail{
    template<typename OutStream, typename T>
    typename std::enable_if<std::is_fundamental<T>::value>::type read(OutStream &stream, T &value){
        stream.read(reinterpret_cast<char *>(&value), sizeof(value));
    }
    
    template<typename OutStream, typename T>
    typename std::enable_if<std::is_same<T,  std::string>::value>::type 
    read(OutStream &stream, T &str){
        std::size_t len;
        read(stream, len);
        str.reserve(len);
        auto buff = std::unique_ptr<char[]>(new char[len]);
        stream.read(buff.get(), len);
        str = std::string(buff.get())+"\0";
    }
    
    template<typename OutStream, typename T>
    typename std::enable_if<(std::is_class<T>::value && !std::is_same<T,  std::string>::value)>::type
    read(OutStream &stream, T &obj){
        obj.read_from(stream);
    }
    
    template<class... Types>
    struct read_proxy{
        read_proxy(Types &... args): args(args...){}
        
        template<typename OutStream>
        void from(OutStream &stream){
            for_each(args, [&](auto &value){
                read(stream, value);
            });
        }
        
        boost::fusion::tuple<Types&...> args;
    };
}

template<class... Types>
auto read(Types&... args){
    return detail::read_proxy<Types...>(args...);
}

Przykład użycia:

#include <iostream>
#include <string>
#include "binary_write.hpp"
#include "binary_read.hpp"
using namespace std;

/* spanish inquisition */
using binarystream = stringstream;

struct Foo{
    int a, b;
    string str;

    void write_to(binarystream &ostream) const{
        write(a, b, str.data()).to(ostream);
    }
    
    void read_from(binarystream &istream){
        read(a, b, str).from(istream);
    }
};

int main(){
    binarystream stream;
    write(1, Foo{2, 3, "Hello World!"}, "Nice to meet you!").to(stream);
    
    int a = 0;
    Foo foo;
    string text;
    
    read(a, foo, text).from(stream);
    
    cout << a << endl
         << "Foo{a="<<foo.a << ", b="<<foo.b << ", str=\""<<foo.str << "\"}" << endl
         << text << endl;
}
1
Foo{a=2, b=3, str="Hello World!"}
Nice to meet you!

1 użytkowników online, w tym zalogowanych: 0, gości: 1