gStore 的 RDF 解析发生在构建数据库时(包括使用 gbuild 和 ghttp 来构建数据库)。如使用 gbuild 构建库:
$ bin/gbuild -db example -f example.nt
则 gStore 会读入 RDF 文件 example.nt ,对其进行解析,将解析得到的结果存入 example 库中。
目前gStore支持的 RDF 数据格式为 Turtle 。
RDF 解析由 RDFParser 类完成。其成员变量包括一个 TurtleParser 类对象:TurtleParser 为开源系统 RDF-3X 提供的 RDF 解析模块(2008 年版本)。RDFParser 以 TurtleParser 的 RDF 解析功能为基础,在此之上补全了符合当前 RDF 标准的合法性检查,并与建库的相应接口适配。
RDFParser::parseFile 为 RDFParser 类中最关键的成员函数,它调用 TurtleParser 解析文件中的三元组,并对成功解析的三元组按照 RDF 1.1 标准的要求进一步进行语法检查。
函数原型:
int parseFile(TripleWithObjType* _triple_array, int& _triple_num, string _error_log="", int init_line=0);
关键代码段说明如下。
将解析过程中的报错输出到日志文件 _error_log
(函数参数)中:
if (!_error_log.empty())
{
if (_error_log == "NULL")
cout.setstate(ios_base::badbit); // Silent output
else
{
ofile.open(_error_log,ios::app);
if (ofile)
{
cout << "Error log file: " << _error_log << endl;
cout.rdbuf(ofile.rdbuf());
}
else
cout << "Error log file cannot be opened." << endl;
}
}
由于采取批量解析的策略,每次调用此函数至多解析 RDFParser::TRIPLE_NUM_PER_GROUP
个三元组:(以下介绍的代码均在此 while 循环中)
while (_triple_num < RDFParser::TRIPLE_NUM_PER_GROUP)
因此,上层会多次调用此函数以完成对整个 RDF 文件的解析(Database::sub2id_pre2id_obj2id_RDFintoSignature函数):
while (true)
{
int parse_triple_num = 0;
_parser.parseFile(triple_array, parse_triple_num);
...
}
调用 TurtleParser ,捕捉异常:
try
{
if (!this->_TurtleParser.parse(rawSubject, rawPredicate, rawObject, _objectType, _objectSubType)) break;
}
catch (const TurtleParser::Exception& _e)
{
...
}
当解析成功的三元组宾语为字面值、且为数值类型(带有数值类型后缀或为数值表示,如 “1”^^
...
else if (_objectType == Type::Type_Integer)
{
try
{
stoll(rawObject);
}
catch (invalid_argument& e)
{
errorMsg = "Object integer value invalid";
cout << "Line " << numLines << " (<" << rawSubject << "> <" << rawPredicate \
<< "> <" << rawObject << ">): " << errorMsg << endl;
continue;
}
catch (out_of_range& e)
{
errorMsg = "Object integer out of range";
cout << "Line " << numLines << " (<" << rawSubject << "> <" << rawPredicate \
<< "> <" << rawObject << ">): " << errorMsg << endl;
continue;
}
_object = "\"" + rawObject + "\"^^";
}
...
TurtleParser::parse 为 TurtleParser 类中最关键的成员函数,它驱动 RDF 三元组解析的全过程。
函数原型:
bool parse(std::string& subject,std::string& predicate,std::string& object,Type::Type_ID& objectType,std::string& objectSubType);
关键代码段说明如下。
根据当前已获取的三元组数量 triplesReader 判断 triples 中是否有已解析但未获取的三元组,若有,提取出其各部分赋值给输入参数,返回真:
// Some triples left?
if (triplesReader=triples.size()) {
triples.clear();
triplesReader=0;
}
return true;
}
否则,判断输入流是否结束(若在遇到任何三元组前结束,则返回假),过程中若遇到 RDF 指令则调用 parseDirective 函数解析,并继续读入:
// No, check if the input is done
Lexer::Token token;
while (true) {
token=lexer.next(subject);
if (token==Lexer::Token_Eof) return false;
// A directive?
if (token==Lexer::Token_At) {
parseDirective();
continue;
} else break;
}
直至遇到三元组数据,将各参数传递给 parseTriple 函数解析之,返回真:
// No, parse a triple
parseTriple(token,subject,predicate,object,objectType,objectSubType);
return true;
本章介绍了 gStore 的 RDF 解析机制,主要详细分析了两个相关类中的核心函数:RDFParser::parseFile 和 TurtleParser::parse(前者调用后者),建议在阅读的同时结合源码 Parser/RDFParser.cpp、Parser/TurtleParser.cpp 一起分析,会更容易理解。接下来我们拟将分析 gStore 的 SPARQL 解析机制。