atoi в массиве символов с большим количеством целых чисел
У меня есть код, в котором массив символов заполняется целыми числами (преобразуются в массивы символов) и читается другой функцией, которая преобразует его обратно в целые числа. Я использовал следующую функцию, чтобы получить преобразование в массив символов:
char data[64];
int a = 10;
std::string str = boost::lexical_cast<std::string>(a);
memcpy(data + 8*k,str.c_str(),sizeof(str.c_str())); //k varies from 0 to 7
и преобразование обратно в символы выполняется с помощью:
char temp[8];
memcpy(temp,data+8*k,8);
int a = atoi(temp);
В целом это работает нормально, но когда я пытаюсь сделать это как часть проекта, включающего qt (версия 4.7), он прекрасно компилируется и выдает мне ошибки сегментации, когда он пытается читать с помощью memcpy(). Обратите внимание, что ошибка сегментации происходит только в цикле чтения, а не во время записи данных. Я не знаю, почему это происходит, но я хочу сделать это любым способом.
Итак, есть ли другие функции, которые я могу использовать, которые могут принимать массив символов, первый бит и последний бит и преобразовывать его в целое число. Тогда мне бы не пришлось использовать memcpy () вообще. Я пытаюсь сделать что-то вроде этого:
new_atoi(data,8*k,8*(k+1)); // k varies from 0 to 7
Заранее спасибо.
2 ответа
Вы копируете только 4 символа (в зависимости от ширины указателя вашей системы). Это оставит числа из 4+ символов, не равные NULL, и приведет к появлению убегающих строк на входе в atoi.
sizeof(str.c_str()) //i.e. sizeof(char*) = 4 (32 bit systems)
должно быть
str.length() + 1
Или символы не будут обнуляться
Только STL:
make_testdata()
: увидеть весь путь вниз
Почему вы не используете потоки...?
#include <sstream>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
int main()
{
std::vector<int> data = make_testdata();
std::ostringstream oss;
std::copy(data.begin(), data.end(), std::ostream_iterator<int>(oss, "\t"));
std::stringstream iss(oss.str());
std::vector<int> clone;
std::copy(std::istream_iterator<int>(iss), std::istream_iterator<int>(),
std::back_inserter(clone));
//verify that clone now contains the original random data:
//bool ok = std::equal(data.begin(), data.end(), clone.begin());
return 0;
}
Вы могли бы сделать это намного быстрее в простом C с atoi/itoa и некоторыми настройками, но я считаю, что вы должны использовать двоичную передачу (см. Boost Spirit Karma и protobuf для хороших библиотек), если вам нужна скорость.
Повысить карму / ци:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
namespace qi=::boost::spirit::qi;
namespace karma=::boost::spirit::karma;
static const char delimiter = '\0';
int main()
{
std::vector<int> data = make_testdata();
std::string astext;
// astext.reserve(3 * sizeof(data[0]) * data.size()); // heuristic pre-alloc
std::back_insert_iterator<std::string> out(astext);
{
using namespace karma;
generate(out, delimit(delimiter) [ *int_ ], data);
// generate_delimited(out, *int_, delimiter, data); // equivalent
// generate(out, int_ % delimiter, data); // somehow much slower!
}
std::string::const_iterator begin(astext.begin()), end(astext.end());
std::vector<int> clone;
qi::parse(begin, end, qi::int_ % delimiter, clone);
//verify that clone now contains the original random data:
//bool ok = std::equal(data.begin(), data.end(), clone.begin());
return 0;
}
Если бы вы хотели вместо этого выполнять двоичную сериализацию, независимую от архитектуры, вы бы использовали эту крошечную адаптацию, ускоряющую процесс в миллионы раз (см. Тест ниже...):
karma::generate(out, *karma::big_dword, data);
// ...
qi::parse(begin, end, *qi::big_dword, clone);
Повысить сериализацию
Наилучшей производительности можно достичь при использовании Boost Serialization в двоичном режиме:
#include <sstream>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/vector.hpp>
int main()
{
std::vector<int> data = make_testdata();
std::stringstream ss;
{
boost::archive::binary_oarchive oa(ss);
oa << data;
}
std::vector<int> clone;
{
boost::archive::binary_iarchive ia(ss);
ia >> clone;
}
//verify that clone now contains the original random data:
//bool ok = std::equal(data.begin(), data.end(), clone.begin());
return 0;
}
Testdata
(общий для всех версий выше)
#include <boost/random.hpp>
// generates a deterministic pseudo-random vector of 32Mio ints
std::vector<int> make_testdata()
{
std::vector<int> testdata;
testdata.resize(2 << 24);
std::generate(testdata.begin(), testdata.end(), boost::mt19937(0));
return testdata;
}
Ориентиры
Я сравнил
- используя входные данные
2<<24
(33554432) случайные целые числа - не отображает вывод (мы не хотим измерять производительность прокрутки нашего терминала)
- грубые времена были
- STL only версия не так уж и плоха на самом деле на 12.6s
- Текстовая версия Karma/Qi была запущена
в 18 с5.1 с, благодаря подсказке Арлена вgenerate_delimited
:) - Бинарная версия кармы / ци (big_dword) всего за 1.4 с (примерно в
12х3-4 раза быстрее) - Ускорение сериализации занимает около 0,8 с (или при замене текстовых архивов вместо двоичных, около 13 с)
Нет абсолютно никаких причин, чтобы текстовая версия Karma/Qi была медленнее, чем версия STL. Я улучшил @sehe реализацию текстовой версии Карма / Ци, чтобы отразить это утверждение.
Следующая текстовая версия Boost Karma/Qi более чем в два раза быстрее, чем версия STL:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/random.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
namespace ascii = boost::spirit::ascii;
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phoenix = boost::phoenix;
template <typename OutputIterator>
void generate_numbers(OutputIterator& sink, const std::vector<int>& v){
using karma::int_;
using karma::generate_delimited;
using ascii::space;
generate_delimited(sink, *int_, space, v);
}
template <typename Iterator>
void parse_numbers(Iterator first, Iterator last, std::vector<int>& v){
using qi::int_;
using qi::phrase_parse;
using ascii::space;
using qi::_1;
using phoenix::push_back;
using phoenix::ref;
phrase_parse(first, last, *int_[push_back(ref(v), _1)], space);
}
int main(int argc, char* argv[]){
static boost::mt19937 rng(0); // make test deterministic
std::vector<int> data;
data.resize(2 << 24);
std::generate(data.begin(), data.end(), rng);
std::string astext;
std::back_insert_iterator<std::string> out(astext);
generate_numbers(out, data);
//std::cout << astext << std::endl;
std::string::const_iterator begin(astext.begin()), end(astext.end());
std::vector<int> clone;
parse_numbers(begin, end, clone);
//verify that clone now contains the original random data:
//std::copy(clone.begin(), clone.end(), std::ostream_iterator<int>(std::cout, ","));
return 0;
}