关于tinyxml2对换行符的处理
前段时间在项目上遇到了一个比较奇怪的问题,一串带有\r\n
xml经过tinyxml2解析后,把\r\n
解析成了\n
,导致字符串不匹配。
后来拿出源码调试解析了一下,发现上在getText
函数上进行了处理。
结论:
\r\n
替换为\n
\r
替换为\n
\n\r
替换为\n
代码分析
XMLElement::GetText()
获取到当前元素的值,进node->Value()
const char* XMLElement::GetText() const
{
/* skip comment node */
const XMLNode* node = FirstChild();
while (node) {
if (node->ToComment()) {
node = node->NextSibling();
continue;
}
break;
}
if ( node && node->ToText() ) {
return node->Value(); // 进value()
}
return 0;
}
XMLNode::Value()
const char* XMLNode::Value() const
{
// Edge case: XMLDocuments don't have a Value. Return null.
if ( this->ToDocument() )
return 0;
return _value.GetStr(); // 进GetStr()
}
StrPair::GetStr()
- 可以看到当读到
CR(\r)
时,如果下一个字符时LF(\n)
,p
指针直接跳过这两个字符,直接读取到后面的字符,并且将q
指针置为\n
,q
其实时p
的一个前置指针; - 当读到
\r
时,但p + 1
并不是\n
,则p
只跳过当前的\r
,并且\r
被指针q
置成了\n
; - 同样的在下一个
if else
分支中,\n\r
也被处理成了\n
;
同时可以看到在下一个if else
分支对转义符号的处理,有兴趣的可以研究研究。
const char* StrPair::GetStr()
{
TIXMLASSERT( _start );
TIXMLASSERT( _end );
if ( _flags & NEEDS_FLUSH ) {
*_end = 0;
_flags ^= NEEDS_FLUSH;
if ( _flags ) {
const char* p = _start; // the read pointer
char* q = _start; // the write pointer
while( p < _end ) {
if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
// CR-LF pair becomes LF
// CR alone becomes LF
// LF-CR becomes LF
if ( *(p+1) == LF ) {
p += 2;
}
else {
++p;
}
*q = LF;
++q;
}
else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
if ( *(p+1) == CR ) {
p += 2;
}
else {
++p;
}
*q = LF;
++q;
}
else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
// Entities handled by tinyXML2:
// - special entities in the entity table [in/out]
// - numeric character reference [in]
// 中 or 中
if ( *(p+1) == '#' ) {
const int buflen = 10;
char buf[buflen] = { 0 };
int len = 0;
const char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
if ( adjusted == 0 ) {
*q = *p;
++p;
++q;
}
else {
TIXMLASSERT( 0 <= len && len <= buflen );
TIXMLASSERT( q + len <= adjusted );
p = adjusted;
memcpy( q, buf, len );
q += len;
}
}
else {
bool entityFound = false;
for( int i = 0; i < NUM_ENTITIES; ++i ) {
const Entity& entity = entities[i];
if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
&& *( p + entity.length + 1 ) == ';' ) {
// Found an entity - convert.
*q = entity.value;
++q;
p += entity.length + 2;
entityFound = true;
break;
}
}
if ( !entityFound ) {
// fixme: treat as error?
++p;
++q;
}
}
}
else {
*q = *p;
++p;
++q;
}
}
*q = 0;
}
// The loop below has plenty going on, and this
// is a less useful mode. Break it out.
if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
CollapseWhitespace();
}
_flags = (_flags & NEEDS_DELETE);
}
TIXMLASSERT( _start );
return _start;
}