如何遍历向量的特定字符串

时间:2019-07-23 13:13:19

标签: c++ loops vector

我正在努力声明一个包含向量字段的循环,检查它是第一次出现还是跳转到下一个向量,直到该字段包含新字符串。

我的输入文件(.csvx)类似于:

No.; ID; A; B; C;...;Z;
1;1_380; Value; Value; Value;...; Value;
2;1_380; Value; Value; Value;...; Value;
3;1_380; Value; Value; Value;...; Value;
...
41;2_380; Value; Value; Value;...; Value;
42;2_380; Value; Value; Value;...; Value;
...
400000; 6_392; Value; Value; Value;...; Value; 

注意:文件比较大。...

我设法将文件解析为vector<vector<string> >,并以分号分隔行以访问任何字段。 现在,我想访问第一个“ ID”,即1_380并从同一行存储参数,然后转到下一个ID 2_380并再次存储这些参数,依此类推...

到目前为止,这是我的代码:

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <boost/algorithm/string.hpp>

using namespace std;

/*
 * CSVX Reader defined to fetch data from 
 * CSVX file into vectors
 */
class CSVXReader
{
   string fileName, delimiter;
public:
   CSVXReader(string filename, string delm = ";") :
   fileName(filename), delimiter(delm)
   {}
   vector<vector<string> > getData();           //Function to fetch data 
   };                                           //from CSVX file 

/*
 * Parse through CSVX file line by line 
 * and return the data in vector of vector
 * of strings
 */
vector<vector<string> > CSVXReader::getData()
{
   ifstream file(fileName);
   vector<vector<string> > dataList;               //Vector of vector 
                                                   //contains all data

   string line = "";                              
   while (getline(file, line))                  //Iterate through each line 
                                                //and split the content 
                                                //using delimiter
   {
      vector<string> vec;                       //Vector contains a row from 
                                                //input file 
      boost::algorithm::split(vec, line, boost::is_any_of(delimiter));
      dataList.push_back(vec);
   }
file.close();
return dataList;
}


int main(int argc, char** argv) 
{
   CSVXReader reader("file.csvx");                     //Creating an object 
                                                       //of CSVXReader
   vector<vector<string> > dataList = reader.getData();//Get the data from 
                                                       //CSVX file
   for(vector<string> vec : datalist)                  //Loop to go through 
                                                       //each line of 
                                                       //dataList 
                                                       //(vec1,vec2;vec3...)
   if(vec[1] contains "_" && "appears for the first time")
   {store parameters...};
   else{go to next line};
return 0;
}

如您所见,我不知道如何正确声明我的循环... 为了清楚起见,我想检查每个向量“ vec”的第二个字段:它是新的吗? ->如果不是,则存储同一行的数据->跳到下一行,即矢量,直到出现新的ID。

期待任何建议!

3 个答案:

答案 0 :(得分:3)

由于您编写了伪代码,因此很难编写真实代码。

但是,通常,如果要检测是否已经发生某项,可以使用std::unordered_set来实现“首次出现”。

使用您的伪代码:

#include <unordered_set>
//...
std::unordered_set<std::string> stringSet;
//...
for(vector<string>& vec : datalist)
{
    if(vec[1] contains "_" && !stringSet.count(vec[1]))
    {
         //...
         stringSet.insert(vec[1]);
    }
}

该条件检查项目是否在unordered_set中。如果是,则跳过;否则,我们处理该项目并将其添加到unordered_set。

答案 1 :(得分:1)

基本上,您不需要其他答案提供的所有代码。您只需要一条语句即可将数据复制到您想要的位置。

让我们假设您已经在dataList中读取了数据。然后,您定义了一个新的std::vector<std::vector<std::string>> parameter{};,您要在其中存储唯一结果。

libraray算法具有一个称为std:copy_if的函数。仅在谓词(条件)为真时才复制数据。您的条件是一行与上一行不同。然后这是包含新数据的新行,您将复制它。如果某行等于其先前的行数据,则不要复制它。

因此,我们将记住最后一行的重要数据。然后在下一行将数据与存储的值进行比较。如果不同,则存储参数。如果没有,那就没有。每次检查后,我们将当前值分配给最后一个值。作为初始的“最后一个值”,我们将使用一个空字符串。因此,第一行将始终是不同的。该语句将如下所示:

std::copy_if(dataList.begin(), dataList.end(), std::back_inserter(parameter),
    [lastID = std::string{}](const std::vector<std::string> & sv) mutable {
        bool result = (lastID != sv[1]);
        lastID = sv[1];
        return result;
    }
);

因此,当且仅当源向量中的第二个字符串(index = 1)不同于第二个字符串时,我们才将dataList的开头到结尾的所有数据复制到parameter向量中我们过去记得的价值。

相当简单

另一种优化方法是立即整理出正确的参数,而不是首先存储包含所有数据的完整向量,而仅存储必要的数据。这将大大减少必要的内存。

将while循环修改为:

string line = "";                              
string oldValue{};
while (getline(file, line))                 //Iterate through each line 
                                            //and split the content 
                                            //using delimiter
{
    vector<string> vec;                       //Vector contains a row from 
                                                //input file 
    boost::algorithm::split(vec, line, boost::is_any_of(delimiter));

    if (oldValue != vec[1]) {
        dataList.push_back(vec);
    }
    oldValue = vec[1];
}

有了它,您从一开始就可以正确使用它。

其他解决方案如下所示

#include <vector>
#include <iostream>
#include <string>
#include <iterator>
#include <regex>
#include <fstream>
#include <sstream>
#include <algorithm>

std::istringstream testFile{R"(1;1_380; Value1; Value2; Value3; Value4
2;1_380; Value5; Value6; Value7; Value8
3;1_380; Value9 Value10 
41;2_380; Value11; Value12; Value13
42;2_380; Value15
42;2_380; Value16
500;3_380; Value99
400000; 6_392; Value17; Value18; Value19; Value20
400001; 6_392; Value21; Value22; Value23; Value24)"
};


class LineAsVector {    // Proxy for the input Iterator
public:
    // Overload extractor. Read a complete line
    friend std::istream& operator>>(std::istream& is, LineAsVector& lv) {

        // Read a line
        std::string line; lv.completeLine.clear();
        std::getline(is, line); 

        // The delimiter
        const std::regex re(";");

        // Split values and copy into resulting vector
        std::copy(  std::sregex_token_iterator(line.begin(), line.end(), re, -1),
                    std::sregex_token_iterator(),
                    std::back_inserter(lv.completeLine));
        return is; 
    }

    // Cast the type 'CompleteLine' to std::string
    operator std::vector<std::string>() const { return completeLine; }
protected:
    // Temporary to hold the read vector
    std::vector<std::string> completeLine{};
};

int main()
{

    // This is the resulting vector which will contain the result
    std::vector<std::vector<std::string>> parameter{};


    // One copy statement to copy all necessary data from the file to the parameter list
    std::copy_if (
        std::istream_iterator<LineAsVector>(testFile),
        std::istream_iterator<LineAsVector>(),
        std::back_inserter(parameter),
        [lastID = std::string{}](const std::vector<std::string> & sv) mutable {
            bool result = (lastID != sv[1]);
            lastID = sv[1];
            return result;
        }
    );


    // For debug purposes: Show result on screen
    std::for_each(parameter.begin(), parameter.end(), [](std::vector<std::string> & sv) {
        std::copy(sv.begin(), sv.end(), std::ostream_iterator<std::string>(std::cout, " "));
        std::cout << '\n';
        } 
    );
    return 0;
}

请注意:在main函数中,我们用一个语句std::copy_if完成所有操作。在这种情况下,源是一个std::istream,因此是一个std::ifstream(一个文件)或您想要的其他文件。在SO中,我使用std::istringstream是因为我不能在这里使用文件。但这是一样的。只需替换std::istream_iterator中的变量即可。我们使用std::istream_iterator遍历文件。

没人会读这本书真可惜。 。

答案 2 :(得分:0)

好吧伙计,我在玩我的代码,意识到@Armins第二个解决方案(在while循环中修改)不考虑无序列表,即,如果某个元素再次出现很晚,则将其与先前的元素进行比较(oldValue )并插入,尽管它已经存在于我的容器中...

经过一番阅读(显然还会有更多阅读),我倾向于@Paul的unordered_set。我的第一个问题就出现在这里:您为什么不建议使用set?根据我的发现,unordered_set对于搜索操作而言显然更快。在我个人非常有限的头脑中,这很难理解...但是我不想在这里深入探讨。 这是你的原因吗?还是我错过了其他优势?

尽管有您的建议,我还是尝试使用set,在我的情况下,这似乎是一种更好的方法,因为方法更加有序。再次,我的代码无法运行:

set<vector<string> > CSVReader::getData() {

ifstream file(fileName);

set<vector<string> > container;

string line = "";
string uniqueValue{};

while (getline(file, line))                          //Iterate through each line and split the content using delimiter
{
    //Vector contains a row from RAO file
    vector<string> vec;                        
    boost::algorithm::split(vec, line, boost::is_any_of(delimiter));

    uniqueValue = vec[2];

    //Line (or vector) is added to container if the uniqueValue, e.g. 1_380, appears for the first time                   

    if(!container.count(uniqueValue))
    {
        container.insert(vec);
    }

}

file.close();
return container;  
}

错误提示:

error: no matching function for call to 'std::set<std::vector<std::__cxx11::basic_string<char> > >::count(std::__cxx11::string&)'
     if(!localDetails.count(localDetail))

自从我遵循了您的示例以来,我做错了什么?

PS:刚刚阅读了SO政策...希望这个附加问题可以接受