【问题标题】:c++ file io & splitting by separatorc++文件io&按分隔符分割
【发布时间】:2010-09-21 00:18:06
【问题描述】:

我有一个文件,其数据如下:

0,       2,    10
10,       8,    10
10,       10,   10
10,       16,   10
15,       10,   16
17,       10,   16

我希望能够输入文件并将其拆分为三个数组,在此过程中修剪所有多余的空格并将每个元素转换为整数。

由于某种原因,我无法在 c++ 中找到一种简单的方法来执行此操作。我唯一的成功是将每一行输入到一个数组中,然后正则表达式输出所有空格,然后将其拆分。整个过程花了我 20-30 行代码,修改另一个分隔符(例如空格)等很痛苦。

这是我希望在 C++ 中拥有的 python 等价物:

f = open('input_hard.dat')
lines =  f.readlines()
f.close()

#declarations
inint, inbase, outbase = [], [], []

#input parsing
for line in lines:
    bits = string.split(line, ',')
    inint.append(int(bits[0].strip()))
    inbase.append(int(bits[1].strip()))
    outbase.append(int(bits[2].strip()))

在 python 中这样做的易用性是我首先转向它的原因之一。但是,我现在需要在 C++ 中执行此操作,我不想不得不使用我丑陋的 20-30 行代码。

任何帮助将不胜感激,谢谢!

【问题讨论】:

    标签: c++ file-io split


    【解决方案1】:

    类似:

    vector<int> inint;
    vector<int> inbase;
    vector<int> outbase;
    while (fgets(buf, fh)) {
       char *tok = strtok(buf, ", ");
       inint.push_back(atoi(tok));
       tok = strtok(NULL, ", ");
       inbase.push_back(atoi(tok));
       tok = strtok(NULL, ", ");
       outbase.push_back(atoi(tok));
    }
    

    除了错误检查。

    【讨论】:

    • 我会避免这样的“C-ish”解决方案,好吧,美学......但在这种情况下更重要的是,因为 strtok 有一些严重的线程安全问题。正确的代码!
    【解决方案2】:

    std::getline 允许您读取一行文本,并且您可以使用字符串流来解析单个行:

    string buf;
    getline(cin, buf); 
    stringstream par(buf);
    
    char buf2[512];
    par.getline(buf2, 512, ','); /* Reads until the first token. */
    

    一旦将文本行放入字符串中,您实际上可以使用任何您想要的解析函数,甚至是 sscanf(buf.c_str(), "%d,%d'%d", &i1, &i2, &i3) ,通过在带有整数的子字符串上使用 atoi 或通过其他方法。

    如果您知道输入流中不需要的字符,您也可以忽略它们:

    if (cin.peek() == ',')
        cin.ignore(1, ',');
    cin >> nextInt;  
    

    【讨论】:

      【解决方案3】:

      如果您不介意使用 Boost 库...

      #include <string>
      #include <vector>
      #include <boost/lexical_cast.hpp>
      #include <boost/regex.hpp>
      
      std::vector<int> ParseFile(std::istream& in) {
          const boost::regex cItemPattern(" *([0-9]+),?");
          std::vector<int> return_value;
      
          std::string line;
          while (std::getline(in, line)) {
              string::const_iterator b=line.begin(), e=line.end();
              boost::smatch match;
              while (b!=e && boost::regex_search(b, e, match, cItemPattern)) {
                  return_value.push_back(boost::lexical_cast<int>(match[1].str()));
                  b=match[0].second;
              };
          };
      
          return return_value;
      }
      

      从流中提取行,然后使用 Boost::RegEx 库(带有捕获组)从行中提取每个数字。它会自动忽略任何不是有效数字的内容,但可以根据需要进行更改。

      #includes 仍然有大约 20 行,但您可以使用它从文件行中提取任何内容。这是一个简单的例子,我使用几乎相同的代码从数据库字段中提取标签和可选值,唯一的主要区别是正则表达式。

      编辑:哎呀,你想要三个独立的向量。试试这个轻微的修改:

      const boost::regex cItemPattern(" *([0-9]+), *([0-9]+), *([0-9]+)");
      std::vector<int> vector1, vector2, vector3;
      
      std::string line;
      while (std::getline(in, line)) {
          string::const_iterator b=line.begin(), e=line.end();
          boost::smatch match;
          while (b!=e && boost::regex_search(b, e, match, cItemPattern)) {
              vector1.push_back(boost::lexical_cast<int>(match[1].str()));
              vector2.push_back(boost::lexical_cast<int>(match[2].str()));
              vector3.push_back(boost::lexical_cast<int>(match[3].str()));
              b=match[0].second;
          };
      };
      

      【讨论】:

        【解决方案4】:

        在这个例子中没有真正需要使用 boost,因为流可以很好地解决问题:

        int main(int argc, char* argv[])
        {
            ifstream file(argv[1]);
        
            const unsigned maxIgnore = 10;
            const int delim = ',';
            int x,y,z;
        
            vector<int> vecx, vecy, vecz;
        
            while (file)
            {
                file >> x;
                file.ignore(maxIgnore, delim);
                file >> y;
                file.ignore(maxIgnore, delim);
                file >> z;
        
                vecx.push_back(x);
                vecy.push_back(y);
                vecz.push_back(z);
            }
        }
        

        虽然如果我要使用 boost,我更喜欢 tokenizer 的简单性而不是正则表达式...:)

        【讨论】:

          【解决方案5】:

          fscanf 确实没有什么问题,这可能是这种情况下最快的解决方案。而且它和python代码一样简短易读:

          FILE *fp = fopen("file.dat", "r");
          int x, y, z;
          std::vector<int> vx, vy, vz;
          
          while (fscanf(fp, "%d, %d, %d", &x, &y, &z) == 3) {
            vx.push_back(x);
            vy.push_back(y);
            vz.push_back(z);
          }
          fclose(fp);
          

          【讨论】:

          • 干得好。人们忘记了 scanf 的模式匹配。最简单的解决方案是最好的。
          【解决方案6】:

          为什么与 python 中的代码不同:)?

          std::ifstream file("input_hard.dat");
          std::vector<int> inint, inbase, outbase;
          
          while (file.good()){
              int val1, val2, val3;
              char delim;
              file >> val1 >> delim >> val2 >> delim >> val3;
          
              inint.push_back(val1);
              inbase.push_back(val2);
              outbase.push_back(val3);
          }
          

          【讨论】:

            【解决方案7】:

            如果您希望能够扩展到更难的输入格式,您应该考虑精神,提升解析器组合库。

            This page 有一个例子,它几乎可以满足你的需要(虽然有实数和一个向量)

            【讨论】:

              猜你喜欢
              • 2015-07-06
              • 1970-01-01
              • 2017-03-25
              • 2013-11-24
              • 1970-01-01
              • 1970-01-01
              • 2013-12-11
              • 1970-01-01
              • 2020-04-23
              相关资源
              最近更新 更多