C++ Boost Spirit入門教程
一、Boost.Spirit庫介紹
本章介紹庫 Boost.Spirit。 Boost.Spirit 用于開發(fā)文本格式的解析器。例如,您可以使用 Boost.Spirit 開發(fā)解析器來加載配置文件。 Boost.Spirit 也可以用于二進(jìn)制格式,盡管它在這方面的用處有限。
Boost.Spirit 簡化了解析器的開發(fā),因?yàn)楦袷绞怯靡?guī)則描述的。規(guī)則定義格式的外觀——其余的由 Boost.Spirit 完成。您可以將 Boost.Spirit 與正則表達(dá)式進(jìn)行比較,因?yàn)樗梢宰屇幚韽?fù)雜的過程——正則表達(dá)式的模式搜索和 Boost.Spirit 的解析——而無需編寫代碼來實(shí)現(xiàn)該過程。
Boost.Spirit 期望使用解析表達(dá)式語法 (PEG) 來描述規(guī)則。 PEG 與擴(kuò)展巴庫斯-瑙爾形式 (EBNF) 有關(guān)。即使您不熟悉這些語言,本章中的示例也足以幫助您入門。
Boost.Spirit 有兩個(gè)版本。第一個(gè)版本稱為 Spirit.Classic。這個(gè)版本不應(yīng)該再使用了。當(dāng)前版本是 2.5.2。這是本章介紹的版本。
從 2.x 版本開始,Boost.Spirit 可用于生成生成器和解析器。解析器讀取文本格式,生成器編寫它們。 Boost.Spirit 中用于開發(fā)解析器的組件稱為 Spirit.Qi。 Spirit.Karma 是用于開發(fā)生成器的組件。命名空間被相應(yīng)地劃分:用于開發(fā)解析器的類和函數(shù)可以在 boost::spirit::qi 中找到,用于開發(fā)生成器的類和函數(shù)可以在 boost::spirit::karma 中找到。
除了 Spirit.Qi 和 Spirit.Karma,該庫還包含一個(gè)名為 Spirit.Lex 的組件,可用于開發(fā)詞法分析器。
本章的重點(diǎn)是開發(fā)解析器。示例主要使用來自 boost::spirit 和 boost::spirit::qi 的類和函數(shù)。對于這些類和函數(shù),包含頭文件 boost/spirit/include/qi.hpp 就足夠了。
如果您不想包含像 boost/spirit/include/qi.hpp 這樣的主頭文件,您可以單獨(dú)包含來自 boost/spirit/include/ 的頭文件。僅包含此目錄中的頭文件很重要。 boost/spirit/include/ 是用戶界面。其他目錄中的頭文件可以在新的庫版本中更改。
二、boost::spirit::qi::parse()解析格式
Boost.Spirit 提供 boost::spirit::qi::parse() 和 boost::spirit::qi::phrase_parse() 來解析格式。
Example11.1.Usingboost::spirit::qi::parse()
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
int main()
{
std::string s;
std::getline(std::cin, s);
auto it = s.begin();
bool match = qi::parse(it, s.end(), ascii::digit);
std::cout << std::boolalpha << match << '\n';
if (it != s.end())
std::cout << std::string{it, s.end()} << '\n';
}例 11.1 引入了 boost::spirit::qi::parse()。這個(gè)函數(shù)需要兩個(gè)被解析字符串的迭代器和一個(gè)解析器。該示例使用由 Boost.Spirit 提供的解析器 boost::spirit::ascii::digit。這是幾個(gè)字符分類解析器之一。這些解析器測試字符是否屬于某個(gè)類。 boost::spirit::ascii::digit 測試字符是否為 0 到 9 之間的數(shù)字。
該示例傳遞從 std::cin 讀取的字符串的迭代器。請注意,開始迭代器沒有直接傳遞給 boost::spirit::qi::parse()。它存儲在變量 it 中,然后傳遞給 boost::spirit::qi::parse()。這樣做是因?yàn)?boost::spirit::qi::parse() 可能會修改迭代器。
如果您鍵入一個(gè)數(shù)字,然后按 Enter,該示例將顯示 true。如果您輸入兩位數(shù)然后回車,則輸出將為真,后跟第二位數(shù)字。如果你輸入一個(gè)字母然后回車,輸出將是假的,然后是字母。
例 11.1 中使用的解析器 boost::spirit::ascii::digit 只測試一個(gè)字符以查看它是否是數(shù)字。如果第一個(gè)字符是數(shù)字,boost::spirit::qi::parse() 返回 true,否則返回 false。 boost::spirit::qi::parse() 的返回值表示解析器是否成功。
boost::spirit::qi::parse() 如果您輸入多個(gè)數(shù)字,也會返回 true。因?yàn)榻馕銎?boost::spirit::ascii::digit 只測試第一個(gè)字符,所以它會在這樣的字符串上成功。第一個(gè)之后的所有數(shù)字都將被忽略。
為了讓您確定可以成功解析多少字符串,boost::spirit::qi::parse() 更改了它的迭代器。調(diào)用 boost::spirit::qi::parse() 后,它指向最后一個(gè)解析成功后的字符。如果輸入多個(gè)數(shù)字,則指第二個(gè)數(shù)字。如果您只輸入一位數(shù)字,則它等于 s 的結(jié)束迭代器。如果你輸入一個(gè)字母,它指的是那個(gè)字母。
boost::spirit::qi::parse() 不會忽略空格。如果運(yùn)行示例 11.1 并輸入空格,則會顯示 false。 boost::spirit::qi::parse() 測試第一個(gè)輸入的字符,即使該字符是空格。如果你想忽略空格,使用 boost::spirit::qi::phrase_parse() 而不是 boost::spirit::qi::parse()。
Example11.2.Usingboost::spirit::qi::phrase_parse()
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
int main()
{
std::string s;
std::getline(std::cin, s);
auto it = s.begin();
bool match = qi::phrase_parse(it, s.end(), ascii::digit, ascii::space);
std::cout << std::boolalpha << match << '\n';
if (it != s.end())
std::cout << std::string{it, s.end()} << '\n';
}boost::spirit::qi::phrase_parse() 的工作方式與 boost::spirit::qi::parse() 類似,但需要另一個(gè)名為 skipper 的參數(shù)。船長是應(yīng)該被忽略的字符的解析器。示例 11.2 使用 boost::spirit::ascii::space,一個(gè)字符分類解析器來檢測空格,作為船長。
boost::spirit::ascii::space 丟棄空格作為分隔符。如果您開始該示例并輸入一個(gè)空格后跟一個(gè)數(shù)字,則顯示為 true。與前面的示例不同,解析器 boost::spirit::ascii::digit 不應(yīng)用于空格,而是應(yīng)用于不是空格的第一個(gè)字符。
請注意,此示例忽略了任意數(shù)量的空格。因此,如果您輸入多個(gè)空格后跟一個(gè)數(shù)字, boost::spirit::qi::phrase_parse() 將返回 true。
與 boost::spirit::qi::parse() 一樣,boost::spirit::qi::phrase_parse() 修改了作為第一個(gè)參數(shù)傳遞的迭代器。這樣,您就知道解析器能夠成功工作到字符串多遠(yuǎn)。示例 11.2 跳過成功解析字符后出現(xiàn)的空格。如果您輸入一個(gè)數(shù)字后跟一個(gè)空格,然后是一個(gè)字母,迭代器將引用該字母,而不是它前面的空格。如果您希望迭代器引用空間,請將 boost::spirit::qi::skip_flag::dont_postskip 作為另一個(gè)參數(shù)傳遞給 boost::spirit::qi::phrase_parse()。
Example11.3.phrase_parse()withboost::spirit::qi::skip_flag::dont_postskip
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
int main()
{
std::string s;
std::getline(std::cin, s);
auto it = s.begin();
bool match = qi::phrase_parse(it, s.end(), ascii::digit, ascii::space,
qi::skip_flag::dont_postskip);
std::cout << std::boolalpha << match << '\n';
if (it != s.end())
std::cout << std::string{it, s.end()} << '\n';
}示例 11.3 將 boost::spirit::qi::skip_flag::dont_postskip 傳遞給 boost::spirit::qi::phrase_parse() 以告訴解析器不要跳過在成功解析數(shù)字之后但在第一個(gè)不成功數(shù)字之前出現(xiàn)的空格解析的字符。如果你輸入一個(gè)數(shù)字后跟一個(gè)空格再跟一個(gè)字母,它指的是調(diào)用 boost::spirit::qi::phrase_parse() 之后的空格。
標(biāo)志 boost::spirit::qi::skip_flag::postskip 是默認(rèn)值,如果 boost::spirit::qi::skip_flag::dont_postskip 和 boost::spirit::qi::skip_flag 都不是,則使用該標(biāo)志: :postskip 已指定。
Example11.4.boost::spirit::qi::phrase_parse()with wide strings
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
int main()
{
std::wstring s;
std::getline(std::wcin, s);
auto it = s.begin();
bool match = qi::phrase_parse(it, s.end(), ascii::digit, ascii::space,
qi::skip_flag::dont_postskip);
std::wcout << std::boolalpha << match << '\n';
if (it != s.end())
std::wcout << std::wstring{it, s.end()} << '\n';
}boost::spirit::qi::parse() 和 boost::spirit::qi::phrase_parse() 接受迭代器到一個(gè)寬字符串。示例 11.4 與前面的示例類似,只是使用了寬字符串。
Boost.Spirit 還支持來自 C++11 標(biāo)準(zhǔn)庫的字符串類型 std::u16string 和 std::u32string。
三、解析器
本節(jié)說明如何定義解析器。您通常從 Boost.Spirit 訪問現(xiàn)有的解析器——例如 boost::spirit::ascii::digit 或 boost::spirit::ascii::space。通過組合解析器,您可以解析更復(fù)雜的格式。該過程類似于定義正則表達(dá)式,它們也是由基本構(gòu)建塊構(gòu)建的。
Example11.5.A parser for two consecutive digits
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
int main()
{
std::string s;
std::getline(std::cin, s);
auto it = s.begin();
bool match = qi::phrase_parse(it, s.end(), ascii::digit >> ascii::digit,
ascii::space);
std::cout << std::boolalpha << match << '\n';
if (it != s.end())
std::cout << std::string{it, s.end()} << '\n';
}示例 11.5 測試是否輸入了兩個(gè)數(shù)字。 boost::spirit::qi::phrase_parse() 僅在兩個(gè)數(shù)字連續(xù)時(shí)才返回 true??崭癖缓雎?。
與前面的示例一樣, boost::spirit::ascii::digit 用于識別數(shù)字。因?yàn)?boost::spirit::ascii::digit 只測試一個(gè)字符,所以解析器使用了兩次來測試兩位數(shù)字的輸入。要連續(xù)兩次使用 boost::spirit::ascii::digit,必須使用運(yùn)算符。 Boost.Spirit 為解析器重載 operator>>。使用 ascii::digit >> ascii::digit 創(chuàng)建了一個(gè)解析器,用于測試字符串是否包含兩個(gè)數(shù)字。
如果您運(yùn)行該示例并輸入兩位數(shù),則會顯示 true。如果您只輸入一位數(shù)字,該示例將顯示為 false。
請注意,如果您在兩位數(shù)之間輸入空格,該示例也會顯示 true。無論在解析器中使用運(yùn)算符 operator>> 的任何位置,都允許使用被船長忽略的字符。因?yàn)槭纠?11.5 使用 boost::spirit::ascii::space 作為跳過符,所以您可以在兩個(gè)數(shù)字之間輸入任意數(shù)量的空格。
如果您希望解析器僅在兩個(gè)數(shù)字之間沒有空格的情況下才接受它們,請使用 boost::spirit::qi::parse() 或指令 boost::spirit::qi::lexeme。
Example11.6.Parsing character by character withboost::spirit::qi::lexeme
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
int main()
{
std::string s;
std::getline(std::cin, s);
auto it = s.begin();
bool match = qi::phrase_parse(it, s.end(),
qi::lexeme[ascii::digit >> ascii::digit], ascii::space);
std::cout << std::boolalpha << match << '\n';
if (it != s.end())
std::cout << std::string{it, s.end()} << '\n';
}示例 11.6 使用解析器 qi::lexeme[ascii::digit >> ascii::digit]。現(xiàn)在, boost::spirit::qi::phrase_parse() 僅在數(shù)字之間沒有空格時(shí)才返回 true。
boost::spirit::qi::lexeme 是可以改變解析器行為的幾個(gè)指令之一。如果你想禁止在使用 operator>> 時(shí)會被船長忽略的字符,你可以使用 boost::spirit::qi::lexeme。
Example11.7.Boost.Spirit rules similar to regular expressions
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
int main()
{
std::string s;
std::getline(std::cin, s);
auto it = s.begin();
bool match = qi::phrase_parse(it, s.end(), +ascii::digit, ascii::space);
std::cout << std::boolalpha << match << '\n';
if (it != s.end())
std::cout << std::string{it, s.end()} << '\n';
}例 11.7 用 +ascii::digit 定義了一個(gè)解析器,它至少需要一個(gè)數(shù)字。這種語法,特別是加號 (+),類似于正則表達(dá)式中使用的語法。加號標(biāo)識一個(gè)字符或字符組,該字符或字符組預(yù)計(jì)在字符串中至少出現(xiàn)一次。如果您啟動示例并輸入至少一位數(shù)字,則會顯示 true。數(shù)字是否由空格分隔并不重要。如果解析器應(yīng)該只接受沒有空格的數(shù)字,請?jiān)俅问褂?boost::spirit::qi::lexeme。
Example11.8.Numeric parsers
#include <boost/spirit/include/qi.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
int main()
{
std::string s;
std::getline(std::cin, s);
auto it = s.begin();
bool match = qi::phrase_parse(it, s.end(), qi::int_, ascii::space);
std::cout << std::boolalpha << match << '\n';
if (it != s.end())
std::cout << std::string{it, s.end()} << '\n';
}示例 11.8 需要一個(gè)整數(shù)。 boost::spirit::qi::int_ 是一個(gè)可以識別正整數(shù)和負(fù)整數(shù)的數(shù)值解析器。與 boost::spirit::ascii::digit 不同,boost::spirit::qi::int_ 可以將多個(gè)字符(例如 +1 或 -23)識別為整數(shù)。
Boost.Spirit 提供了額外的邏輯解析器。 boost::spirit::qi::float_、boost::spirit::qi::double_ 和 boost::spirit::qi::bool_ 是可以讀取浮點(diǎn)數(shù)和布爾值的數(shù)值解析器。使用 boost::spirit::qi::eol,您可以測試行尾字符。 boost::spirit::qi::byte_ 和 boost::spirit::qi::word 可用于讀取一個(gè)或兩個(gè)字節(jié)。 boost::spirit::qi::word 和其他二進(jìn)制解析器識別平臺的字節(jié)順序并進(jìn)行相應(yīng)的解析。如果要基于特定的字節(jié)序進(jìn)行解析,無論平臺如何,都可以使用 boost::spirit::qi::little_word 和 boost::spirit::qi::big_word 等解析器。
到此這篇關(guān)于C++ Boost Spirit入門教程的文章就介紹到這了,更多相關(guān)C++ Boost Spirit內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c++ 連接兩個(gè)字符串實(shí)現(xiàn)代碼 實(shí)現(xiàn)類似strcat功能
c++ 連接兩個(gè)字符串實(shí)現(xiàn)代碼 實(shí)現(xiàn)類似strcat功能,需要的朋友可以參考下2012-05-05
用C實(shí)現(xiàn)PHP擴(kuò)展 Fetch_Url 類數(shù)據(jù)抓取的方法
該擴(kuò)展是基于libcurl基礎(chǔ)實(shí)現(xiàn)的網(wǎng)頁數(shù)據(jù)抓取2013-04-04
C++實(shí)現(xiàn)批量提取PDF內(nèi)容
這篇文章主要為大家詳細(xì)介紹了如何使用C++批量提取PDF里文字內(nèi)容并導(dǎo)出到表格以及批量給?PDF?文件改名,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02
C++ API功能設(shè)計(jì)的實(shí)現(xiàn)
C++ API中看似很小的修改,都可能會影響到生成的對象和庫文件的二進(jìn)制表示,如果客戶想替換共享庫使之工作,就不能簡單的替換庫文件了事,而往往需要重新編譯2022-08-08
實(shí)例講解C++設(shè)計(jì)模式編程中State狀態(tài)模式的運(yùn)用場景
這篇文章主要介紹了實(shí)例講解C++設(shè)計(jì)模式編程中State狀態(tài)模式的運(yùn)用場景,文章最后的適用性部分則介紹了一些State模式善于處理的情況,需要的朋友可以參考下2016-03-03
C++使用CriticalSection實(shí)現(xiàn)線程同步實(shí)例
這篇文章主要介紹了C++使用CriticalSection實(shí)現(xiàn)線程同步實(shí)例,是使用CriticalSection對前文實(shí)例的擴(kuò)展,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-10-10
關(guān)于C++復(fù)制構(gòu)造函數(shù)的實(shí)現(xiàn)講解
今天小編就為大家分享一篇關(guān)于關(guān)于C++復(fù)制構(gòu)造函數(shù)的實(shí)現(xiàn)講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12

