RapidXML 페이지 에서 주워 온 version 1.13의 ZIP 파일을 풀어 나온 것은 헤더가 4 개, HTML 문서 및 라이센스 조항 ...... 이만큼? 정말 무정한 구성이지만 헤더를 #include하는 것만으로 사용할 것, 경량 라이브러리의 귀감입니다. 헤더의 타임 스탬프가 2009 년 5 월에서今日日컴파일러가 솔직하게 먹고 줄까 조금 불안 했습니다만, 시험 코드를 Visual C ++ 2017이するりと받아 들여 주었으므로 우선 안심.
parse의 김기홍
RapidXML에서 XML을 구문 분석하는 것은 매우 간단하다. xml_document을 준비하고 멤버 함수 : parse ()에 XML 문자열을 먹여뿐.
#include <rapidxml.hpp> #include <iostream> #include <string> using namespace std ; // 이후 RapidXML 네임 스페이스 : rapidxml을 rx로 단축 namespace rx = rapidxml ; int main () { string xml = R "(<function include ="<cstdio> "> <return type =" int "/> printf </ function>)" "\ 0" ; // '\ 0'끝을 보장 rx : xml_document <> doc ; doc . parse < 0 > (& xml [ 0 ]); // 인수는 char * // 루트 아래 <function> 노드 및 그 특성에 대해 rx : xml_node <> * node = doc . first_node ( "function" ); rx : xml_attribute <> * attr = node -> first_attribute (); // 이름과 값을 출력 cout << "node :" << node -> name () << "=" << node -> value () << "\ n" ; cout << "attr : " << attr -> name () << "= " << attr -> value () << "\ n " ; cout << endl ; for ( char ch : xml ) { cout << ( ch ! = '\ 0' ? ch : '#' ); } cout << endl ; }
"function"라고 이름 붙여진 노드와 노드에 포함 된 "include"속성 (attribute)의 이름과 값을 출력합니다.
조금 이상한 행동을 보이고 있군요 노드 : xml_node의 멤버 함수 : value ()이 노드가 내포 자식 노드 중 처음 발견 된 텍스트 노드의 값 : "printf"를 반환합니다.
또한 parse ()에 들지시킨 XML 문자열을 ( '\ 0'을 '#'로 대체) 출력 해 보면 곳곳 '\ 0'을 포함하거나 "& lt;"/ "& gt;"을 " < "/"> "로 대체하고 있습니다. RapidXML 파서에 준 문자열을 데이터 영역으로 유용 할 것입니다 (따라서 parse ()의 인수는 const char * 아니고 char *). RapidXML에 들지시킨 문자열 파서를 사용 끝날 때까지 개방하거나 재사용하거나 안돼입니다.
라는 것은 즉, 텍스트 파일에 기록 된 XML을 구문 분석 할 때 입력되는 텍스트 파일의 내용을 통째로 문자 영역에 보관 해 두지 냐 안됩니다. 헤더 : <rapidxml_utils.hpp>는 파일의 내용을 유지 해주는 도우미 클래스 : file이 정의되어 있습니다. 이걸로 XML 파일을 파싱 루트 노드 : person 바로 아래의 자식 노드를 열거 해 보겠습니다.
#include <rapidxml.hpp> #include <rapidxml_utils.hpp> // rapidxml :: file #include <iostream> using namespace std ; namespace rx = rapidxml ; int main () { rx : xml_document <> doc ; rx : file <> input ( "trial.xml" ); doc . parse < 0 > ( input . data ()); rx : xml_node <> * node = doc . first_node ( "person" ); for ( rx : xml_node <> * child = node -> first_node (); child ! = nullptr ; child = child -> next_sibling () ) { cout << child -> name () << ":" << child -> value () << "\ n" ; } }
<? xml version = "1.0" encoding = "shift_jis" ?> gender = "female" age = "52" 후네 gender = "female" age = "24" 소라 gender = "male" age = "4" 대구 gender = "male" age = "11" 가다랭이 gender = "female" age = " 9 " 미역 gender = "male " age = "? " 타마
텍스트의 전후에 불필요한 공백이 붙어 남아 있지만이를 제거 할 수 있습니다. parse () 템플릿 인수로 퍼스 옵션을 지정할 수 있습니다, parse <rapidxml :: parse_trim_whitespace> (input.data ()); 그러면이 같습니다.
퍼스 옵션은 1 개 정도 정의되어 있습니다 (설명서를 읽어보십시오).
RapidXML은 문자 코드 변환 해주지 않습니다. 이 샘플에서는 먹지시킨 trial.xml가 shift-jis이므로 (Windows에서) 문제가 없지만 XML은 UTF-8로 쓰는 것이 많기 때문에 적절히 문자 코드의 변환이 필요합니다. 예시 UTF-8 파일을 먹여 보니 당연히 바케바케이었습니다.
또한 퍼스시 오류를 감지하면 parse_error 예외가 throw됩니다. parse_error에서 오류 이유 : what ()과 그 위치 : where ()을 얻을 수 있습니다. where ()가 반환 문자열에서 오류로 판정 된 위치 (포인터)입니다.
#include <rapidxml.hpp> #include <iostream> #include <string> using namespace std ; namespace rx = rapidxml ; int main () { // ↓ 여기에 "..."가 없다! string xml = R "(<function include => <return type =" int "/> printf </ function>)" "\ 0" ; rx : xml_document <char> doc ; try { doc . parse < 0 > (& xml [ 0 ]); } catch ( const rx : parse_error & ex ) { cerr <<ex . what () << "@ [" << ex . where <char> () << "\ n" ; } }
traverse : 노드간에 이동
XML은 tree 구조를 표현하는 것이므로 tree를 구성하는 절 (노드) 사이를 떠돌아 다니는 멤버 함수가 준비되어 있습니다.
- xml_base :: parent () : 부모 노드 (xml_base는 xml_node / xml_attribute의 base-class)
- xml_node :: first_node () : 최초의 아이 (장자) 노드
- xml_node :: last_node () : 마지막 자식 (막내) 노드
- xml_node :: next_sibling () : 형제 관계에있는 다음 (동생) 노드
- xml_node :: previous_sibling () : 형제 관계에있는 전 (오빠) 노드
따라서, 특정 노드에 대하여 그 자식 노드를 순방향 / 역방향으로 열거한다면 :
// 순 방향 xml_node <> * node = ... for ( xml_node <> * child = node -> first_node (); child ! = nullptr ; child = child -> next_sibling () ) { // child 대해 뭔가 하는 } // 역방향 xml_node <> * node = ... for ( xml_node <> * child = node -> last_node (); child ! = nullptr ; child = child -> previous_sibling () ) { // child 대해 뭔가 하는 }
속성 (xml_attribute)도 마찬가지로,
- 첫 번째 특성 : xml_node :: first_attribute ()
- 마지막 속성 : xml_node :: first_attribute ()
- 다음 속성 : xml_attribute :: next_sibling ()
- 이전 속성 : xml_attribute :: previous_sibling ()
이러한 xml_node / xml_attribute을 떠돌아 다니는 멤버 함수 군은 인수에 문자열을 주어지면 그 문자열과 노드 이름 / 속성 이름이 일치하는 것으로 한정 할 수 있습니다.
#include <rapidxml.hpp> #include <rapidxml_utils.hpp> #include <iostream> using namespace std ; namespace rx = rapidxml ; // name를 이름에 가지는 노드를 재귀 적으로 열거 void walk ( rx : xml_node <> * node , const char * name ) { for ( rx : xml_node <> * child = node -> first_node ( name ) ; child ! = nullptr ; child = child -> next_sibling ( name ) ) { cout << child -> name () << ":" << child -> value () << "\ n" ; walk ( child , name ); } } int main () { rx : xml_document <> doc ; rx : file <> input ( "trial.xml" ); doc . parse < rx : parse_trim_whitespace > ( input . data ()); walk (& doc , "person" ); // xml_document는 xml_node 파생 클래스 }
노드 이름 / 속성 이름을 지정하지 않으면 앞으로 열거하면 헤더 : <rapidxml_iterators.hpp>에 정의 된 이터레이터 : node_iterator / attribute_iterator를 사용하면 조금 편해집니다.
#include <rapidxml.hpp> #include <rapidxml_utils.hpp> #include <rapidxml_iterators.hpp> #include <iostream> #include <algorithm> using namespace std ; namespace rx = rapidxml ; // 노드를 재귀 적으로 열거 template < typename Function > void walk ( rx : xml_node <> * node , Function fun ) { for_each ( rx : node_iterator <char> ( node ) rx : node_iterator <char> () [&] ( rx : xml_node <> & child ) { fun ( child ); walk (& child , fun ); }); } int main () { rx : xml_document <> doc ; rx : file <> input ( "trial.xml" ); doc . parse < rx : parse_trim_whitespace > ( input . data ()); walk (& doc , [] ( rx : xml_node <> & node ) { // 요소 노드이면, 이름과 값 및 속성 이름 / 속성 값을 출력하는 if ( node . type () == rx : node_element ) { cout << node . name () << ":" << node . value () << "]" ; for_each ( rx : attribute_iterator <char> (& node ) rx :: attribute_iterator <char> (), [] ( const rx : xml_attribute <> &attr ) { cout << attr . name () << "=" << attr . value () << "" ; }); cout << endl ; } }); }
node_iterator / attribute_itterator을 반환 멤버 함수 begin () / end ()를 가진 도우미 클래스 또는 전역 함수 std :: begin () / std :: end ()를 정의 해두면 range-based for 사용할 수 있습니다 주세요 :
#include <rapidxml.hpp> #include <rapidxml_utils.hpp> #include <rapidxml_iterators.hpp> template < typename Ch = char > class children { typedef rapidxml :: node_iterator < Ch > iterator_type ; public : explicit children ( rapidxml :: xml_node < Ch > * node ) : node_ ( node ) {} iterator_type begin () const { return iterator_type ( node_ ); } iterator_type end () const { return iterator_type (); } private : rapidxml :: xml_node < Ch > * node_ ; }; template < typename Ch = char > class attributes { typedef rapidxml :: attribute_iterator < Ch > iterator_type ; public : explicit attributes ( rapidxml :: xml_node < Ch > * node ) : node_ ( node ) {} iterator_type begin () const { return iterator_type ( node_ ); } iterator_type end () const { return iterator_type (); } private : rapidxml :: xml_node < Ch > * node_ ; }; namespace std { template < typename Ch > inline rapidxml :: node_iterator < Ch > begin ( rapidxml :: node_iterator < Ch > iter ) { return iter ; }; template < typename Ch > inline rapidxml :: node_iterator < Ch > end ( rapidxml :: node_iterator < Ch > iter ) { return rapidxml :: node_iterator < Ch > (); }; } namespace std { template < typename Ch > inline rapidxml :: attribute_iterator < Ch > begin ( rapidxml :: attribute_iterator < Ch > iter ) { return iter ; }; template < typename Ch > inline rapidxml :: attribute_iterator < Ch > end ( rapidxml :: attribute_iterator < Ch > iter ) { return rapidxml :: attribute_iterator < Ch > (); }; } #include <iostream> using namespace std ; namespace rx = rapidxml ; // 노드를 재귀 적으로 열거 template < typename Function > void walk ( rx : xml_node <> * node , Function fun ) { for ( rx : xml_node <> & child : children <> ( node ) ) { fun ( child ); walk (& child , fun ); } } int main () { rx : xml_document <> doc ; rx : file <> input ( "trial.xml" ); doc . parse < rx : parse_trim_whitespace > ( input . data ()); walk (& doc , [] ( rx : xml_node <> & node ) { // 요소 노드이면, 이름과 값 및 속성 이름 / 속성 값을 출력하는 if ( node . type () == rx : node_element ) { cout << node . name () << ":" << node . value () << "]" ; for ( const rx : xml_attribute <> & attr : rx :: attribute_iterator <char> (& node )) { cout << attr . name () << "=" <<attr . value () << "" ; } cout << endl ; } }); }