解析数字字符串
我需要解析包含由单个空格字符分隔的4个浮点数字的几个C样式字符串(大约500k)。下面是一个字符串的例子:解析数字字符串
“90292 5879 89042.2576 5879”
我需要这些数字存储在代表两个点的两个结构。考虑到字符串可以在解析时被修改,并且99.99%的数字只是无符号整数,那么最快的方法是什么?
以下是我目前的执行情况:
#include <iostream>
#include <cassert>
#include <chrono>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
using namespace chrono;
struct PointF
{
float x;
float y;
};
void parse_points(char* points, PointF& p1, PointF& p2)
{
auto start = points;
const auto end = start + strlen(points);
// p1.x
start = std::find(start, end, ' ');
assert(start < end);
*start = '\0';
p1.x = static_cast<float>(atof(points));
points = start + 1;
// p1.y
start = std::find(start, end, ' ');
assert(start < end);
*start = '\0';
p1.y = static_cast<float>(atof(points));
points = start + 1;
// p2.x
start = std::find(start, end, ' ');
assert(start < end);
*start = '\0';
p2.x = static_cast<float>(atof(points));
points = start + 1;
// p2.y
start = std::find(start, end, ' ');
assert(start == end);
p2.y = static_cast<float>(atof(points));
}
int main()
{
const auto n = 500000;
char points_str[] = "90292 5879 89042.2576 5879";
PointF p1, p2;
vector<string> data(n);
for (auto& s : data)
s.assign(points_str);
const auto t0 = system_clock::now();
for (auto i = 0; i < n; i++)
parse_points(const_cast<char*>(data[i].c_str()), p1, p2);
const auto t1 = system_clock::now();
const auto elapsed = duration_cast<milliseconds>(t1 - t0).count();
cout << "Elapsed: " << elapsed << " ms" << endl;
cin.get();
return 0;
}
鉴于浮点值的字符串,空格分隔:
const std::string example_input = "90292 5879 89042.2576 5879";
你就应该剖析,看看有什么更快,阅读浮点:
std::istringstream text_stream(example_input);
std::vector<double> container;
double value;
while (text_stream >> value)
{
container.push_back(value);
}
或读为整数,服用性能损失,如果有浮点的指示:
std::istringstream text_stream(example_input);
std::vector<double> container;
double value;
signed int int_value;
std::streampos position_before_read = text_stream.tellg();
while (text_stream >> int_value)
{
// check the next character for possible floating point differences.
char c;
text_stream >> c;
switch (c)
{
case '.':
case 'E': case 'e':
// Rewind to before the number and read as floating point
text_stream.seekg(position_before_read);
text_stream >> value;
break;
default:
value = 1.0 * int_value;
break;
}
container.push_back(value);
position_before_read = text_stream.tellg();
}
我的猜测是,标准库进行优化,以读取浮点的,比上面的例子中更好占所有浮点格式的差异。
注意:或者,您可以将小数和指数读取为整数(如果存在),然后使用全部三部分构建浮点值。
你有没有测量过我的解决方案? – Nick
不,你是否对所有浮标的简单读数测量了您的解决方案? –
您可以实施atof
返回space
的pos。这样,你只需要遍历每个字符串一次。
例如。
char *atof(char *point, float &num) {
num = 0;
bool neg = false, dot = false;
float decimal = 0, mul = 0.1;
if (*point == '-') {
neg = true;
point++;
} else if (*point == '+') {
point++;
}
while (*point != ' ' && *point) {
if (*point == '.') {
dot = true;
} else {
if (dot) {
decimal += (*point - '0') * mul;
mul *= 0.1;
} else {
num = num * 10 + *point - '0';
}
}
point++;
}
if (dot) {
num += decimal;
}
if (neg) {
num = -num;
}
return point;
}
使用strtod;不要重新发明*。 – rici
我看到多个问题的代码(和它的实际好,你已经问):
- 没有错误的情况下,操作时有号码(注:按讨论,你在这种情况下,预计0)
- 您创建的PointF对象两次,能够通过他们
- 您将它们作为参考,因此它不是平凡的人读取调用代码,这些都出来了PARAMS。
- 您所创建的解析器在C语言中(虽然你可能会测量,如果它的速度更快或更慢)
我建议这样的:(注意:std::experimental::optional<>
相当于这里boost::optional<>
)
#include <iostream>
#include <cstring>
#include <utility>
#include <experimental/optional>
struct PointF
{
float x;
float y;
};
std::experimental::optional<std::pair<PointF, PointF>> parse_points(char* pch)
{
pch = strtok (pch, " ");
if (pch != NULL)
{
float x0 = atof(pch);
pch = strtok (NULL, " ");
if (pch != NULL)
{
float y0 = atof(pch);
pch = strtok (NULL, " ");
if (pch != NULL)
{
float x1 = atof(pch);
pch = strtok (NULL, " ");
if (pch != NULL)
{
float y1 = atof(pch);
PointF p0{x0, y0}, p1{x1, y1};
return std::make_pair(p0, p1);
}
}
}
}
return std::experimental::nullopt;
}
int main() {
const char str[] ="90292 5879 89042.2576 5879";
char* pch0 = new char[sizeof(str)], *pch = pch0;
memcpy(pch0, str, sizeof(str));
std::experimental::optional<std::pair<PointF, PointF>> pOpt(parse_points(pch0));
if(pOpt)
std::cout << pOpt->first.x << " " << pOpt->first.y << " "
<< pOpt->second.x << " " << pOpt->second.y << " " << std::endl;
delete pch;
}
不幸的是'std :: optional'在C++ 11中不可用(我甚至不能测试它)。你有没有测试过我的解决方案?你得到了什么样的改善? – Nick
atof'在出错的情况下返回0,这很好,因为它应该是默认值。除非在这种情况下不影响性能,否则始终接受清晰度。 – Nick
@Nick:是的,'等于'部分丢失了:它是'boost :: optional',你也可以用它作为C++ 03。解决这个问题。至于'atof'和默认值0,不清楚'0x12 AB AF xx'应该导致((0,0),(0,0))还是(我认为)这是一个错误。另外 - 如果默认值改变(例如((-1,-1),(-1,-1)))? – lorro
这是我的版本,没有strlen
,但使用strtok_s
。 在我的机器上,它需要1.1sec
而不是1.5sec
。
void parse_points(char* points, PointF& p1, PointF& p2)
{
char *next_token1 = nullptr;
// p1.x
points = strtok_s(points, " ", &next_token1);
p1.x = points ? static_cast<float>(atof(points)) : 0.0f;
// p1.y
points = strtok_s(nullptr, " ", &next_token1);
p1.y = points ? static_cast<float>(atof(points)) : 0.0f;
// p2.x
points = strtok_s(nullptr, " ", &next_token1);
p2.x = points ? static_cast<float>(atof(points)) : 0.0f;
// p2.y
points = strtok_s(nullptr, " ", &next_token1);
p2.y = points ? static_cast<float>(atof(points)) : 0.0f;
}
int main()
{
const auto n = 500000;
char points_str[] = "90292 5879 89042.2576 5879";
PointF p1, p2;
vector<string> data(n);
for (auto& s : data)
s.assign(points_str);
const auto t0 = system_clock::now();
for (auto i = 0; i < n; i++)
parse_points(const_cast<char*>(data[i].c_str()), p1, p2);
const auto t1 = system_clock::now();
const auto elapsed = duration_cast<milliseconds>(t1 - t0).count();
cout << "Elapsed: " << elapsed << " ms" << endl;
//cin.get();
return 0;
}
我猜'boost :: lexical_cast'比'atof'快。 –
@sorosh_sabz实际上慢了8倍以上.... – Nick
解析问题太多,至少你可以做的就是先搜索。试试这个:[“* C++读取文件空间分隔浮动”](https://www.google.com/search?q=*+c%2B%2B+read+file+space+separated+float&ie=utf-8&oe = utf-8) –