日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C++?Boost?Spirit入門教程_C 語言

作者:無水先生 ? 更新時間: 2022-12-10 編程語言

一、Boost.Spirit庫介紹

本章介紹庫 Boost.Spirit。 Boost.Spirit 用于開發文本格式的解析器。例如,您可以使用 Boost.Spirit 開發解析器來加載配置文件。 Boost.Spirit 也可以用于二進制格式,盡管它在這方面的用處有限。

Boost.Spirit 簡化了解析器的開發,因為格式是用規則描述的。規則定義格式的外觀——其余的由 Boost.Spirit 完成。您可以將 Boost.Spirit 與正則表達式進行比較,因為它可以讓您處理復雜的過程——正則表達式的模式搜索和 Boost.Spirit 的解析——而無需編寫代碼來實現該過程。

Boost.Spirit 期望使用解析表達式語法 (PEG) 來描述規則。 PEG 與擴展巴庫斯-瑙爾形式 (EBNF) 有關。即使您不熟悉這些語言,本章中的示例也足以幫助您入門。

Boost.Spirit 有兩個版本。第一個版本稱為 Spirit.Classic。這個版本不應該再使用了。當前版本是 2.5.2。這是本章介紹的版本。

從 2.x 版本開始,Boost.Spirit 可用于生成生成器和解析器。解析器讀取文本格式,生成器編寫它們。 Boost.Spirit 中用于開發解析器的組件稱為 Spirit.Qi。 Spirit.Karma 是用于開發生成器的組件。命名空間被相應地劃分:用于開發解析器的類和函數可以在 boost::spirit::qi 中找到,用于開發生成器的類和函數可以在 boost::spirit::karma 中找到。

除了 Spirit.Qi 和 Spirit.Karma,該庫還包含一個名為 Spirit.Lex 的組件,可用于開發詞法分析器。

本章的重點是開發解析器。示例主要使用來自 boost::spirit 和 boost::spirit::qi 的類和函數。對于這些類和函數,包含頭文件 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()。這個函數需要兩個被解析字符串的迭代器和一個解析器。該示例使用由 Boost.Spirit 提供的解析器 boost::spirit::ascii::digit。這是幾個字符分類解析器之一。這些解析器測試字符是否屬于某個類。 boost::spirit::ascii::digit 測試字符是否為 0 到 9 之間的數字。

該示例傳遞從 std::cin 讀取的字符串的迭代器。請注意,開始迭代器沒有直接傳遞給 boost::spirit::qi::parse()。它存儲在變量 it 中,然后傳遞給 boost::spirit::qi::parse()。這樣做是因為 boost::spirit::qi::parse() 可能會修改迭代器。

如果您鍵入一個數字,然后按 Enter,該示例將顯示 true。如果您輸入兩位數然后回車,則輸出將為真,后跟第二位數字。如果你輸入一個字母然后回車,輸出將是假的,然后是字母。

例 11.1 中使用的解析器 boost::spirit::ascii::digit 只測試一個字符以查看它是否是數字。如果第一個字符是數字,boost::spirit::qi::parse() 返回 true,否則返回 false。 boost::spirit::qi::parse() 的返回值表示解析器是否成功。

boost::spirit::qi::parse() 如果您輸入多個數字,也會返回 true。因為解析器 boost::spirit::ascii::digit 只測試第一個字符,所以它會在這樣的字符串上成功。第一個之后的所有數字都將被忽略。

為了讓您確定可以成功解析多少字符串,boost::spirit::qi::parse() 更改了它的迭代器。調用 boost::spirit::qi::parse() 后,它指向最后一個解析成功后的字符。如果輸入多個數字,則指第二個數字。如果您只輸入一位數字,則它等于 s 的結束迭代器。如果你輸入一個字母,它指的是那個字母。

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 的參數。船長是應該被忽略的字符的解析器。示例 11.2 使用 boost::spirit::ascii::space,一個字符分類解析器來檢測空格,作為船長。

boost::spirit::ascii::space 丟棄空格作為分隔符。如果您開始該示例并輸入一個空格后跟一個數字,則顯示為 true。與前面的示例不同,解析器 boost::spirit::ascii::digit 不應用于空格,而是應用于不是空格的第一個字符。

請注意,此示例忽略了任意數量的空格。因此,如果您輸入多個空格后跟一個數字, boost::spirit::qi::phrase_parse() 將返回 true。

與 boost::spirit::qi::parse() 一樣,boost::spirit::qi::phrase_parse() 修改了作為第一個參數傳遞的迭代器。這樣,您就知道解析器能夠成功工作到字符串多遠。示例 11.2 跳過成功解析字符后出現的空格。如果您輸入一個數字后跟一個空格,然后是一個字母,迭代器將引用該字母,而不是它前面的空格。如果您希望迭代器引用空間,請將 boost::spirit::qi::skip_flag::dont_postskip 作為另一個參數傳遞給 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() 以告訴解析器不要跳過在成功解析數字之后但在第一個不成功數字之前出現的空格解析的字符。如果你輸入一個數字后跟一個空格再跟一個字母,它指的是調用 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。

三、解析器

本節說明如何定義解析器。您通常從 Boost.Spirit 訪問現有的解析器——例如 boost::spirit::ascii::digit 或 boost::spirit::ascii::space。通過組合解析器,您可以解析更復雜的格式。該過程類似于定義正則表達式,它們也是由基本構建塊構建的。

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 測試是否輸入了兩個數字。 boost::spirit::qi::phrase_parse() 僅在兩個數字連續時才返回 true。空格被忽略。

與前面的示例一樣, boost::spirit::ascii::digit 用于識別數字。因為 boost::spirit::ascii::digit 只測試一個字符,所以解析器使用了兩次來測試兩位數字的輸入。要連續兩次使用 boost::spirit::ascii::digit,必須使用運算符。 Boost.Spirit 為解析器重載 operator>>。使用 ascii::digit >> ascii::digit 創建了一個解析器,用于測試字符串是否包含兩個數字。

如果您運行該示例并輸入兩位數,則會顯示 true。如果您只輸入一位數字,該示例將顯示為 false。

請注意,如果您在兩位數之間輸入空格,該示例也會顯示 true。無論在解析器中使用運算符 operator>> 的任何位置,都允許使用被船長忽略的字符。因為示例 11.5 使用 boost::spirit::ascii::space 作為跳過符,所以您可以在兩個數字之間輸入任意數量的空格。

如果您希望解析器僅在兩個數字之間沒有空格的情況下才接受它們,請使用 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]。現在, boost::spirit::qi::phrase_parse() 僅在數字之間沒有空格時才返回 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 定義了一個解析器,它至少需要一個數字。這種語法,特別是加號 (+),類似于正則表達式中使用的語法。加號標識一個字符或字符組,該字符或字符組預計在字符串中至少出現一次。如果您啟動示例并輸入至少一位數字,則會顯示 true。數字是否由空格分隔并不重要。如果解析器應該只接受沒有空格的數字,請再次使用 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 需要一個整數。 boost::spirit::qi::int_ 是一個可以識別正整數和負整數的數值解析器。與 boost::spirit::ascii::digit 不同,boost::spirit::qi::int_ 可以將多個字符(例如 +1 或 -23)識別為整數。

Boost.Spirit 提供了額外的邏輯解析器。 boost::spirit::qi::float_、boost::spirit::qi::double_ 和 boost::spirit::qi::bool_ 是可以讀取浮點數和布爾值的數值解析器。使用 boost::spirit::qi::eol,您可以測試行尾字符。 boost::spirit::qi::byte_ 和 boost::spirit::qi::word 可用于讀取一個或兩個字節。 boost::spirit::qi::word 和其他二進制解析器識別平臺的字節順序并進行相應的解析。如果要基于特定的字節序進行解析,無論平臺如何,都可以使用 boost::spirit::qi::little_word 和 boost::spirit::qi::big_word 等解析器。

原文鏈接:https://yamagota.blog.csdn.net/article/details/127327156

欄目分類
最近更新