我正在努力声明一个包含向量字段的循环,检查它是第一次出现还是跳转到下一个向量,直到该字段包含新字符串。
我的输入文件(.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。
期待任何建议!
答案 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政策...希望这个附加问题可以接受