将一串数字解析为int向量的最快方法
Quickest Way to parse a string of numbers into a vector of ints
我想知道将一串数字解析为int向量的最快方法是什么。我的情况是,我将有数百万行数据,格式如下:
>Header-name
ID1 1 1 12
ID2 3 6 234
.
.
.
>Header-name
ID1 1 1 12
ID2 3 6 234
.
.
.
我想放弃"Header name"字段(或者稍后使用它进行排序),然后忽略ID字段,然后将剩余的三个int放入向量中。我意识到,我可以在几个带有逻辑的for循环中使用boost拆分和词法转换来忽略某些数据,但我不确定这是否会给我带来最快的解决方案。我看过boost spirit,但我真的不知道如何使用它。boost或STL都可以。
你必须使用boost吗?这个功能我已经用了一段时间了。我相信我是从Accelerated C++中得到的,并且从那以后一直在使用它。您的分隔符似乎是一个制表符或多个空格。如果将分隔符传递给",它可能会起作用。不过,我认为这将取决于实际情况。
std::vector<std::string> split( const std::string& line, const std::string& del )
{
std::vector<std::string> ret;
size_t i = 0;
while ( i != line.size() ) {
while ( ( i != line.size() ) && ( line.substr(i, 1) == del ) ) {
++i;
}
size_t j = i;
while ( ( j != line.size() ) && ( line.substr(j, 1) != del ) ) {
++j;
}
if ( i != j ) {
ret.push_back( line.substr( i, j - i ) );
i = j;
}
}
return ret;
}
你可以用这个得到每一行:
int main() {
std::string line;
std::vector<std::string> lines;
while ( std::getline( std::cin, line ) ) {
lines.push_back( line );
}
for ( auto it = lines.begin(); it != lines.end(); it++ ) {
std::vector<string> vec = split( (*it) );
// Do something
}
}
您可以通过快速修改使其返回std::vector。使用atoi(myString.c_str())将每个字符串设为int此外,您还需要签入以跳过标题。应该是琐碎的。
请注意,我没有在上面编译过。)
关于这个特定的问题,如果你想要最快的,我建议你一次手动解析1个字符。Boost Spirit可能会紧随其后,为您节省大量丑陋的代码。
一次手动解析一个字符是高速的关键,因为即使是像atoi和strtol这样优化良好的转换器也必须处理许多不同的数字表示,而您的示例似乎意味着您只对纯无符号整数感兴趣。格式化的IO(scanf、运算符<<等)非常慢。将行读入中间字符串可能会产生明显的成本。
您的问题很简单,可以手动解析,假设标题行不包含任何"\t"(并且假设没有任何IO或格式错误):
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
std::vector<unsigned> parse(std::istream &is)
{
bool skipField = true;
char c;
unsigned value = 0;
std::vector<unsigned> result;
while (is.get(c))
{
if (('t' == c) || ('n' == c))
{
if (!skipField)
{
result.push_back(value);
}
skipField = ('n' == c);
value = 0;
}
else if (!skipField)
{
value *= 10;
value += (c - '0');
}
}
return result;
}
int main()
{
const std::string data = ">Header-namenID1t1t1t12nID2t3t6t234n";
std::istringstream is(data);
const std::vector<unsigned> v = parse(is);
for (unsigned u: v)
{
std::cerr << u << std::endl;
}
}
和往常一样,对于这样令人愉快的未指定问题,没有什么比展示做"一件事"的"方法"更多的了。在这种情况下,我使用了Boost Spirit(因为你提到了它):
解析为扁平容器
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <map>
std::string const input(
">Header - name1n"
"ID1 1 1 12n"
"ID2 3 6 234n"
">Header - name2n"
"ID3 3 3 14n"
"ID4 5 8 345n"
);
using Header = std::string;
using Container = std::vector<int>;
using Data = std::map<Header, Container>;
int main()
{
namespace qi = boost::spirit::qi;
auto f(input.begin()), l(input.end());
Data data;
bool ok = qi::phrase_parse(f, l,
*(
'>' >> qi::raw[*(qi::char_ - qi::eol)] >> qi::eol
>> *(!qi::char_('>') >> qi::omit[qi::lexeme[+qi::graph]] >> *qi::int_ >> qi::eol)
), qi::blank, data);
if (ok)
{
std::cout << "Parse successn";
for (auto const& entry : data)
{
std::cout << "Integers read with header '" << entry.first << "':n";
for (auto i : entry.second)
std::cout << i << " ";
std::cout << "n";
}
}
else
{
std::cout << "Parse failedn";
}
if (f != l)
std::cout << "Remaining input: '" << std::string(f, l) << "'n";
}
打印
Parse success
Integers read with header 'Header - name1':
1 1 12 3 6 234
Integers read with header 'Header - name2':
3 3 14 5 8 345
正在解析到嵌套容器中
当然,如果你想为每一行单独的矢量(不要期望效率),那么你可以简单地替换typedef:
using Container = std::list<std::vector<int> >; // or any other nested container
// to make printing work without further change:
std::ostream& operator<<(std::ostream& os, std::vector<int> const& v)
{
os << "[";
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(os, " "));
return os << "]";
}
打印
Parse success
Integers read with header 'Header - name1':
[1 1 12 ] [3 6 234 ]
Integers read with header 'Header - name2':
[3 3 14 ] [5 8 345 ]
您只能使用以下内容,而不是我使用的字符串数组,您将从文件中获取字符串
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
int main()
{
std::string s[] = { "ID1 1 1 12", "ID2 3 6 234" };
std::vector<int> v;
for ( const std::string &t : s )
{
std::istringstream is( t );
std::string tmp;
is >> tmp;
v.insert( v.end(), std::istream_iterator<int>( is ),
std::istream_iterator<int>() );
}
for ( int x : v ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
输出为
1 1 12 3 6 234
至于标头,您可以检查tmp是否是标头,如果是,您将跳过此记录。
这是一个简化版
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
int main()
{
std::string s[] =
{
"ID1 1 1 12",
">Header-name",
"ID2 3 6 234"
};
std::vector<int> v;
for ( const std::string &t : s )
{
std::istringstream is( t );
std::string tmp;
is >> tmp;
if ( tmp[0] == '>' ) continue;
v.insert( v.end(), std::istream_iterator<int>( is ),
std::istream_iterator<int>() );
}
for ( int x : v ) std::cout << x << ' ';
std::cout << std::endl;
return 0;
}
输出将与上述相同。
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- EASTL矢量<向量<int>>连续的
- 如何写向量的无序向量集,即unordered_set<向量<向量<int>>集合?
- 为什么我不能返回向量<向量<int>>(进程返回 -1073741819 (0xC0000005))
- 如何在没有循环的C++中总结向量 int 的向量
- C++:向量<向量<int>>迭代器?
- C++:在向量<int> vn{MyAllocator<int>(a)} 中调用哪些构造函数?
- 初始化一个类型向量的巨大向量<int>
- adjacent_difference算法,back_inserter向量<向量<int>>
- 如何在函数中将向量<int>v[n] 作为变速器传递?
- 将向量<int>转换为整数
- 如何正确地将向量<int>转换为空隙*并返回向量<int>?
- 有没有一种优雅的方法来读取二进制文件并将其内容复制到向量<int>
- 从<string>向量<int>中定义的索引处的向量中删除字符串
- 如何使用另一个"Eigen::VectorXd"向量中的值初始化"Eigen::VectorXd",基于索引的"向量<int>"
- 有没有办法将向量<double>*指向向量<int>?
- 如何在不复制的情况下更新 std::unordered_map<std::string, std::vector> 中的向量<int>?
- 向量的向量 int 向量
- 在向量中仅制作外部向量<向量<int>>固定
- 如何在 c++ 中将质因数向量<int >减少到映射<int,int>?