如何在C ++中创建通用数据标记器?

时间:2015-10-12 15:03:58

标签: c++ string parsing c++11

所以,假设我有一个数据文件,其常规数据格式与此类似:

[42,6,9,56,1337]
[220,9001,15,22,35]
[127,0,0,1,8080]

我以字符串形式读取每一行,并且我有一个标记生成器,它接受一个输入字符串,多个分隔符作为另一个字符串,以及对vector<string>的引用,用于存储输出。

// given a string with delimiters inside, parse it into
//  individual tokens stored in a vector<string>
void tokenize(const string& str, vector<string>& tokens,
              const string& delimiters = " ") {
  auto last_pos = str.find_first_not_of(delimiters, 0);      // first token
  auto curr_pos = str.find_first_of(delimiters, last_pos);   // next delim

  while (curr_pos != str_end || last_pos != str_end) {
    tokens.emplace_back(str.substr(last_pos, curr_pos - last_pos));    
    last_pos = str.find_first_not_of(delimiters, curr_pos);  // next token
    curr_pos = str.find_first_of(delimiters, last_pos);      // next delim
  }
}

int main() {
  ifstream fs{"data"};
  string tmp{""};
  const string delims{"[,]"};
  vector<string> tokens;
  //vector<int> tokens;
  //vector<double> tokens;

  while (getline(fs, tmp)) tokenize(tmp, tokens, delims);

  cout << tokens << endl;
}

到目前为止确定。但后来我想使用实际的数据类型而不是字符串,所以我编写了几个数字包装函数,它们将vector<string>转换为(比方说)vector<int>。然后我意识到这些基本上是彼此重复的。

// int wrapper
void tokenize(const string& str, vector<int>& tokens,
              const string& delimiters = " ") {
  vector<string> str_tokens;
  tokenize(str, str_tokens, delims);

  for (const auto& e : str_tokens)
    tokens.emplace_back(stoi(e));  // ints    
}

然后我尝试创建另一个通用包装器,但我对这些问题感到困惑 A )我不确定如何在标准库转换函数之间进行更改,以及 B )想象它也会尝试用字符串T执行,这不是最初的想法。

经过一番深思,我意识到我可能只是做错了,应该尝试使用一个通用函数代替。但我不知道如何做到这一点。

这是节目列表。数据存储为名为just&#34; data&#34;的本地文件。 http://pastebin.com/dRAXRWa3

2 个答案:

答案 0 :(得分:0)

这是模板发挥作用的典型示例。唯一的罪魁祸首是你需要调用不同的函数来将字符串转换为数据类型。这也可以通过模板解决。这是一个有效的评论示例:

#include <iostream>
#include <iomanip>

#include <vector>
#include <algorithm>
#include <string>

using namespace std;

// Declare a generic conversion function...
template<typename T>
T stoT(const std::string& s);

// ... and specialize it for the data types you need to convert
// int specialization
template<>
int stoT(const std::string& s)
{
    return stoi(s);
}

// double specialization
template<>
double stoT(const std::string& s)
{
    return stod(s);
}

template<typename T>
void tokenize(const string& str, vector<T>& tokens,
              const string& delimiters = " ") {
    vector<string> str_tokens = {"1", "2", "3"};

    // Prepare the output - clear and reserve the space to avoid multiple allocations
    str_tokens.clear();
    tokens.reserve(str_tokens.size());

    // Transform the strings to your data types
    std::transform(str_tokens.begin(), str_tokens.end(), std::back_inserter(tokens), stoT<T>);

}

int main()
{
    std::vector<int> vi;
    tokenize("", vi); 
    for (const auto& v : vi) { std::cout << v << " "; }

    std::cout << "\n";

    std::vector<double> vd;
    tokenize("", vd); 
    std::cout << std::fixed;
    for (const auto& v : vd) { std::cout << std::setprecision(2) << v << " "; }
}

答案 1 :(得分:0)

所以一个伙伴把我链接到isocpp:template specialization的这个页面,我能够想出一个可行的方法(虽然Rostislav的显然更好)。

我创建了一个

T decode<T>(const string& x) { }

一组特化,然后模板化tokenizer()函数并更改了一行代码。

tokens.emplace_back(decode<T>(str.substr(last_pos, curr_pos - last_pos)));

它似乎与我的意图非常相似。现在我会根据你的建议改进它。

感谢。

(编辑)这是一个更正版本。 http://pastebin.com/reRMc2G3