我想以通用的方式在C ++中读取和解析文本文件。该文件始终由键值对组成,每行一个。关键是模板化和价值。我预见键和值总是一个基本类型(int,float,string)。
我的问题是我不知道如何将键或值字符串转换为正确的类型。
我尝试了以下内容:
template<class Key, class T> inline
void EventReportReader<Key, T>::validateFileFormat()
{
// Read file line by line and check that the first token is of type Key and the second one of type T
std::string line;
try {
boost::regex re( "(\\S+)\\s+(.*)" );
while( getline( inStream_, line ) ) {
boost::cmatch matches;
if( boost::regex_match( line.c_str(), matches, re ) ) {
std::cout << re << " matches " << line << std::endl;
std::cout << " 1st : " << matches[1] << "\n 2nd : " << matches[2] << std::endl;
// test types
Key *k = dynamic_cast<Key*>(&matches[1]);
T t = dynamic_cast<T>(matches[2]);
}
}
}
catch( boost::regex_error& e ) {
// todo problem with regular expression, abort
}
}
使用这种方法如下:
// This in turn calls the method validateFileFormat
EventReportReader<float, int> reader( testFileName );
结果是
/home/vonhalle/dev/EventBasedReport/libs/event_based_report/EventReportReader.h:121:60:错误:不能使用BidiIterator进行dynamic_cast'(const boost :: sub_match *)matches.boost :: match_results :: operator [] = const char *,Allocator = std :: allocator&gt;,boost :: match_results :: const_reference = const boost :: sub_match&amp;'(类型为'const struct boost :: sub_match ')以键入'float '(目标不是指针或类的引用) /home/vonhalle/dev/EventBasedReport/libs/event_based_report/EventReportReader.h:122:53:错误:不能使用BidiIterator = const char *来动态匹配match.boost :: match_results :: operator [],Allocator = std :: allocator &gt;,boost :: match_results :: const_reference = const boost :: sub_match&amp;'(类型'const struct boost :: sub_match')输入'int'(目标不是指针或引用)
我该怎么办? 它甚至可能吗?
修改 如果模板是&lt;该文件可能看起来像这样。 float,int&gt;
1.14 5
2.34 78
0.56 24
或者如果模板是&lt; int,string&gt;
23 asdf
45 2222
1 bbbb
EDIT2:
上述问题陈述部分错误。密钥永远不是字符串,值可以是字符串。因此,在第一个空格之前是关键,其余的是值。抱歉这个错误。
答案 0 :(得分:4)
我认为你的基本方法是错误的 您似乎正在尝试使用模板元编程来实现您的目标 这可能不是一个好主意。
更简单的方法就是使用C ++流 这些流对象已经知道如何读取所有基本类型。任何想要在C ++中做任何事情的人都会添加适当的输入和输出运算符来传递他们的类;因此,您可以将任何类型作为键和值读取(具有必须适合一行的限制),这是非常普遍的。
所以现在你只需要使用标准模板逻辑来定义一个操作符,它将在一行中读取不同类型的两个对象。
试试这个:
#include <string>
#include <memory>
#include <fstream>
#include <sstream>
#include <vector>
#include <iterator>
#include <algorithm>
// These can be any types.
typedef std::string Key;
typedef int Value;
// The data type to hold the data.
template<typename K,typename V>
class Data: public std::pair<K, V>
{
};
以下是从文件的一行读取一条记录的代码:
请注意,数据类型Data
和此输入运算符都是模板化的,因此可以准备任何对象的键/值对(只要这些对象知道如何自己流式传输)。
template<typename K,typename V>
std::istream& operator>>(std::istream& stream, Data<K,V>& data)
{
// Read a line into a local string.
std::string line;
std::getline(stream,line);
// convert the line into a stream and read the key/value from the line
std::stringstream linestream(line);
linestream >> data.first >> data.second;
// If the linestream is bad, then reading the key/value failed
// If reading one more `char` from the linestream works then there is extra crap in the line
// thus we have bad data on a line.
//
// In either case set the bad bit for the input stream.
char c;
if ((!linestream) || (linestream >> c))
{
stream.setstate(std::ios::badbit);
}
// return the stream.
return stream;
}
现在使用它只是意味着使用流:
int main()
{
// The input file
std::ifstream file("Plop");
// We will convert the file and store it into this vector.
std::vector<Data<Key,Value> > data;
// Now just copy the data from the stream into the vector.
std::copy(std::istream_iterator<Data<Key,Value> >(file),
std::istream_iterator<Data<Key, Value> >(),
std::back_inserter(data)
);
}
注意:在上面的示例中,键必须是单个单词(因为它是使用字符串读取的)。如果您希望将键作为包含空格的字符串,则需要执行一些额外的工作。但这是另一个问题的主题。
答案 1 :(得分:2)
dynamic_cast
将用于继承层次结构;你在这里滥用它。
对于这样一个简单的任务,你可以使用流,我认为(警告,未经测试):
template<class Key, class T>
void EventReportReader<Key, T>::validateFileFormat()
{
std::string line;
while( getline( inStream_, line ) ) {
std::istringstream stream(line);
Key k; T t;
stream >> k >> t;
if (!stream || !stream.eof()) { /*error*/ }
}
}
是否有理由不实际保留您阅读的值?
编辑:让我们专注于字符串
template<class Key>
void EventReportReader<Key, std::string>::validateFileFormat()
{
std::string line;
while( getline( inStream_, line ) ) {
size_t const ws = line.find(' ');
if (ws == std::string::npos) { /* error */ }
std::istringstream stream(line.substr(0, ws));
Key k;
stream >> k;
if (!stream) { /*error*/ }
std::string t = line.substr(ws);
boost::trim(t);
}
}
答案 2 :(得分:1)
如果没有信息,您无法确定自己是否拥有合适的类型。您的值可以是字符串或int。例如,您可以使用此算法对其进行解析:
同样,您可以成功解析int,但是您需要一个字符串。
关于您的实施:您可能需要部分模板专业化。
template<class Key> inline
void EventReportReader<Key, int>::validateFileFormat()
{
// readLine
// parse as int..
}
template<class Key> inline
void EventReportReader<Key, float>::validateFileFormat()
{
// readLine
// parse as float..
}
但最好不要模拟你的功能?如果你有一个“普通”功能,你可以使用我之前描述的解析逻辑。