C++ Boost Spirit入門教程
一、Boost.Spirit庫介紹
本章介紹庫 Boost.Spirit。 Boost.Spirit 用于開發(fā)文本格式的解析器。例如,您可以使用 Boost.Spirit 開發(fā)解析器來加載配置文件。 Boost.Spirit 也可以用于二進制格式,盡管它在這方面的用處有限。
Boost.Spirit 簡化了解析器的開發(fā),因為格式是用規(guī)則描述的。規(guī)則定義格式的外觀——其余的由 Boost.Spirit 完成。您可以將 Boost.Spirit 與正則表達式進行比較,因為它可以讓您處理復(fù)雜的過程——正則表達式的模式搜索和 Boost.Spirit 的解析——而無需編寫代碼來實現(xiàn)該過程。
Boost.Spirit 期望使用解析表達式語法 (PEG) 來描述規(guī)則。 PEG 與擴展巴庫斯-瑙爾形式 (EBNF) 有關(guān)。即使您不熟悉這些語言,本章中的示例也足以幫助您入門。
Boost.Spirit 有兩個版本。第一個版本稱為 Spirit.Classic。這個版本不應(yī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,該庫還包含一個名為 Spirit.Lex 的組件,可用于開發(fā)詞法分析器。
本章的重點是開發(fā)解析器。示例主要使用來自 boost::spirit 和 boost::spirit::qi 的類和函數(shù)。對于這些類和函數(shù),包含頭文件 boost/spirit/include/qi.hpp 就足夠了。
如果您不想包含像 boost/spirit/include/qi.hpp 這樣的主頭文件,您可以單獨包含來自 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()。這個函數(shù)需要兩個被解析字符串的迭代器和一個解析器。該示例使用由 Boost.Spirit 提供的解析器 boost::spirit::ascii::digit。這是幾個字符分類解析器之一。這些解析器測試字符是否屬于某個類。 boost::spirit::ascii::digit 測試字符是否為 0 到 9 之間的數(shù)字。
該示例傳遞從 std::cin 讀取的字符串的迭代器。請注意,開始迭代器沒有直接傳遞給 boost::spirit::qi::parse()。它存儲在變量 it 中,然后傳遞給 boost::spirit::qi::parse()。這樣做是因為 boost::spirit::qi::parse() 可能會修改迭代器。
如果您鍵入一個數(shù)字,然后按 Enter,該示例將顯示 true。如果您輸入兩位數(shù)然后回車,則輸出將為真,后跟第二位數(shù)字。如果你輸入一個字母然后回車,輸出將是假的,然后是字母。
例 11.1 中使用的解析器 boost::spirit::ascii::digit 只測試一個字符以查看它是否是數(shù)字。如果第一個字符是數(shù)字,boost::spirit::qi::parse() 返回 true,否則返回 false。 boost::spirit::qi::parse() 的返回值表示解析器是否成功。
boost::spirit::qi::parse() 如果您輸入多個數(shù)字,也會返回 true。因為解析器 boost::spirit::ascii::digit 只測試第一個字符,所以它會在這樣的字符串上成功。第一個之后的所有數(shù)字都將被忽略。
為了讓您確定可以成功解析多少字符串,boost::spirit::qi::parse() 更改了它的迭代器。調(diào)用 boost::spirit::qi::parse() 后,它指向最后一個解析成功后的字符。如果輸入多個數(shù)字,則指第二個數(shù)字。如果您只輸入一位數(shù)字,則它等于 s 的結(jié)束迭代器。如果你輸入一個字母,它指的是那個字母。
boost::spirit::qi::parse() 不會忽略空格。如果運行示例 11.1 并輸入空格,則會顯示 false。 boost::spirit::qi::parse() 測試第一個輸入的字符,即使該字符是空格。如果你想忽略空格,使用 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() 類似,但需要另一個名為 skipper 的參數(shù)。船長是應(yīng)該被忽略的字符的解析器。示例 11.2 使用 boost::spirit::ascii::space,一個字符分類解析器來檢測空格,作為船長。
boost::spirit::ascii::space 丟棄空格作為分隔符。如果您開始該示例并輸入一個空格后跟一個數(shù)字,則顯示為 true。與前面的示例不同,解析器 boost::spirit::ascii::digit 不應(yīng)用于空格,而是應(yīng)用于不是空格的第一個字符。
請注意,此示例忽略了任意數(shù)量的空格。因此,如果您輸入多個空格后跟一個數(shù)字, boost::spirit::qi::phrase_parse() 將返回 true。
與 boost::spirit::qi::parse() 一樣,boost::spirit::qi::phrase_parse() 修改了作為第一個參數(shù)傳遞的迭代器。這樣,您就知道解析器能夠成功工作到字符串多遠。示例 11.2 跳過成功解析字符后出現(xiàn)的空格。如果您輸入一個數(shù)字后跟一個空格,然后是一個字母,迭代器將引用該字母,而不是它前面的空格。如果您希望迭代器引用空間,請將 boost::spirit::qi::skip_flag::dont_postskip 作為另一個參數(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ù)字之后但在第一個不成功數(shù)字之前出現(xiàn)的空格解析的字符。如果你輸入一個數(shù)字后跟一個空格再跟一個字母,它指的是調(diào)用 boost::spirit::qi::phrase_parse() 之后的空格。
標志 boost::spirit::qi::skip_flag::postskip 是默認值,如果 boost::spirit::qi::skip_flag::dont_postskip 和 boost::spirit::qi::skip_flag 都不是,則使用該標志: :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() 接受迭代器到一個寬字符串。示例 11.4 與前面的示例類似,只是使用了寬字符串。
Boost.Spirit 還支持來自 C++11 標準庫的字符串類型 std::u16string 和 std::u32string。
三、解析器
本節(jié)說明如何定義解析器。您通常從 Boost.Spirit 訪問現(xiàn)有的解析器——例如 boost::spirit::ascii::digit 或 boost::spirit::ascii::space。通過組合解析器,您可以解析更復(fù)雜的格式。該過程類似于定義正則表達式,它們也是由基本構(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 測試是否輸入了兩個數(shù)字。 boost::spirit::qi::phrase_parse() 僅在兩個數(shù)字連續(xù)時才返回 true??崭癖缓雎浴?/p>
與前面的示例一樣, boost::spirit::ascii::digit 用于識別數(shù)字。因為 boost::spirit::ascii::digit 只測試一個字符,所以解析器使用了兩次來測試兩位數(shù)字的輸入。要連續(xù)兩次使用 boost::spirit::ascii::digit,必須使用運算符。 Boost.Spirit 為解析器重載 operator>>。使用 ascii::digit >> ascii::digit 創(chuàng)建了一個解析器,用于測試字符串是否包含兩個數(shù)字。
如果您運行該示例并輸入兩位數(shù),則會顯示 true。如果您只輸入一位數(shù)字,該示例將顯示為 false。
請注意,如果您在兩位數(shù)之間輸入空格,該示例也會顯示 true。無論在解析器中使用運算符 operator>> 的任何位置,都允許使用被船長忽略的字符。因為示例 11.5 使用 boost::spirit::ascii::space 作為跳過符,所以您可以在兩個數(shù)字之間輸入任意數(shù)量的空格。
如果您希望解析器僅在兩個數(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]?,F(xiàn)在, boost::spirit::qi::phrase_parse() 僅在數(shù)字之間沒有空格時才返回 true。
boost::spirit::qi::lexeme 是可以改變解析器行為的幾個指令之一。如果你想禁止在使用 operator>> 時會被船長忽略的字符,你可以使用 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 定義了一個解析器,它至少需要一個數(shù)字。這種語法,特別是加號 (+),類似于正則表達式中使用的語法。加號標識一個字符或字符組,該字符或字符組預(yù)計在字符串中至少出現(xiàn)一次。如果您啟動示例并輸入至少一位數(shù)字,則會顯示 true。數(shù)字是否由空格分隔并不重要。如果解析器應(yīng)該只接受沒有空格的數(shù)字,請再次使用 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 需要一個整數(shù)。 boost::spirit::qi::int_ 是一個可以識別正整數(shù)和負整數(shù)的數(shù)值解析器。與 boost::spirit::ascii::digit 不同,boost::spirit::qi::int_ 可以將多個字符(例如 +1 或 -23)識別為整數(shù)。
Boost.Spirit 提供了額外的邏輯解析器。 boost::spirit::qi::float_、boost::spirit::qi::double_ 和 boost::spirit::qi::bool_ 是可以讀取浮點數(shù)和布爾值的數(shù)值解析器。使用 boost::spirit::qi::eol,您可以測試行尾字符。 boost::spirit::qi::byte_ 和 boost::spirit::qi::word 可用于讀取一個或兩個字節(jié)。 boost::spirit::qi::word 和其他二進制解析器識別平臺的字節(jié)順序并進行相應(yīng)的解析。如果要基于特定的字節(jié)序進行解析,無論平臺如何,都可以使用 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語言數(shù)據(jù)結(jié)構(gòu)之堆、堆排序的分析及實現(xiàn)
堆是一個近似完全二叉樹的結(jié)構(gòu),并同時滿足堆積的性質(zhì),下面這篇文章主要給大家介紹了關(guān)于C語言數(shù)據(jù)結(jié)構(gòu)之堆、堆排序的分析及實現(xiàn)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-04-04VS Code 中搭建 Qt 開發(fā)環(huán)境方案分享
這篇文章主要介紹了VS Code 中搭建 Qt 開發(fā)環(huán)境方案分享的相關(guān)資料,需要的朋友可以參考下2022-12-12關(guān)于C++出現(xiàn)Bus error問題的排查與解決
項目代碼中經(jīng)常出現(xiàn)莫名其妙的Bus error問題,并且代碼中增加很多try catch 后依然不能將錯誤捕獲,一旦Bus erro出現(xiàn),進程直接崩潰掉,所以本文給大家介紹了關(guān)于C++出現(xiàn)Bus error問題的排查與解決,需要的朋友可以參考下2024-01-01