markup.h + markup.cpp

markup.h and markup.cpp in the http://www.firstobject.com/are the tools for the xml operatin in c++ language.

markup.h

// Markup.h: interface for the CMarkup class. // // Markup Release 6.1 Lite // Copyright (C) 1999-2001 First Objective Software, Inc. All rights reserved // This entire notice must be retained in this source code // Redistributing this source code requires written permission // This software is provided "as is", with no warranty. // Latest fixes enhancements and documentation at www.firstobject.com #if !defined(AFX_MARKUP_H__948A2705_9E68_11D2_A0BF_00105A27C570__INCLUDED_) #define AFX_MARKUP_H__948A2705_9E68_11D2_A0BF_00105A27C570__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include <afxtempl.h> class CMarkup { public: CMarkup() { SetDoc( NULL ); }; CMarkup( LPCTSTR szDoc ) { SetDoc( szDoc ); }; CMarkup( const CMarkup& markup ) { *this = markup; }; void operator=( const CMarkup& markup ); virtual ~CMarkup() {}; // Navigate bool SetDoc( LPCTSTR szDoc ); bool IsWellFormed(); bool FindElem( LPCTSTR szName=NULL ); bool FindChildElem( LPCTSTR szName=NULL ); bool IntoElem(); bool OutOfElem(); void ResetChildPos() { m_iPosChild = 0; }; void ResetPos(); CString GetTagName() const { return x_GetTagName(m_iPos); }; CString GetChildTagName() const { return x_GetTagName(m_iPosChild); }; CString GetData() const { return x_GetData(m_iPos); }; CString GetChildData() const { return x_GetData(m_iPosChild); }; CString GetAttrib( LPCTSTR szAttrib ) const { return x_GetAttrib(m_iPos,szAttrib); }; CString GetChildAttrib( LPCTSTR szAttrib ) const { return x_GetAttrib(m_iPosChild,szAttrib); }; bool GetOffsets( int& nStart, int& nEnd ) const; CString GetError() const { return m_csError; }; // Create CString GetDoc() const { return m_csDoc; }; bool AddElem( LPCTSTR szName, LPCTSTR szData=NULL ); bool AddChildElem( LPCTSTR szName, LPCTSTR szData=NULL ); bool AddAttrib( LPCTSTR szAttrib, LPCTSTR szValue ); bool AddChildAttrib( LPCTSTR szAttrib, LPCTSTR szValue ); protected: CString m_csDoc; int m_nLevel; CString m_csError; struct ElemPos { ElemPos() { Clear(); }; ElemPos( const ElemPos& pos ) { *this = pos; }; bool IsEmptyElement() const { return (nStartR == nEndL + 1); }; void Clear() { nStartL=0; nStartR=0; nEndL=0; nEndR=0; nNext=0; iElemParent=0; iElemChild=0; iElemNext=0; }; int nStartL; int nStartR; int nEndL; int nEndR; int nNext; int iElemParent; int iElemChild; int iElemNext; }; CArray< ElemPos, ElemPos& > m_aPos; int m_iPos; int m_iPosChild; int m_iPosFree; int x_GetFreePos(); int x_ReleasePos(); struct TokenPos { TokenPos() { Clear(); }; bool IsValid() const { return (nL <= nR); }; void Clear() { nL=0; nR=-1; bIsString=false; }; int nL; int nR; int nNext; bool bIsString; }; int x_ParseElem( int iPos ); int x_ParseError( LPCTSTR szError, LPCTSTR szTag = NULL ); bool x_FindChar( int&n, _TCHAR c ) const; bool x_FindToken( TokenPos& token ) const; CString x_GetToken( const TokenPos& token ) const; CString x_GetTagName( int iPos ) const; CString x_GetData( int iPos ) const; CString x_GetAttrib( int iPos, LPCTSTR szAttrib ) const; int x_Add( int iPosParent, int iPosBefore, LPCTSTR szName, LPCTSTR szValue ); bool x_FindAttrib( TokenPos& token, LPCTSTR szAttrib=NULL ) const; int x_AddAttrib( int iPos, LPCTSTR szAttrib, LPCTSTR szValue ); int x_SetAttrib( int iPos, LPCTSTR szAttrib, LPCTSTR szValue ); bool x_SetData( int iPos, LPCTSTR szData, int nCDATA ); void x_DocChange( int nLeft, int nReplace, const CString& csInsert ); void x_PosInsert( int iPos, int nInsertLength ); void x_Adjust( int iPos, int nShift ); CString x_TextToDoc( LPCTSTR szText, bool bAttrib = false ) const; CString x_TextFromDoc( int nLeft, int nRight ) const; }; #endif // !defined(AFX_MARKUP_H__948A2705_9E68_11D2_A0BF_00105A27C570__INCLUDED_)

markup.cpp

// Markup.cpp: implementation of the CMarkup class. // // Markup Release 6.1 Lite // Copyright (C) 1999-2001 First Objective Software, Inc. All rights reserved // This entire notice must be retained in this source code // Redistributing this source code requires written permission // This software is provided "as is", with no warranty. // Latest fixes enhancements and documentation at www.firstobject.com #include "stdafx.h" #include "afxconv.h" #include "Markup.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif void CMarkup::operator=( const CMarkup& markup ) { m_iPos = markup.m_iPos; m_iPosChild = markup.m_iPosChild; m_iPosFree = markup.m_iPosFree; m_aPos.RemoveAll(); m_aPos.Append( markup.m_aPos ); m_nLevel = markup.m_nLevel; m_csDoc = markup.m_csDoc; } void CMarkup::ResetPos() { // Reset the main and child positions m_iPos = 0; m_iPosChild = 0; m_nLevel = 0; }; bool CMarkup::SetDoc( LPCTSTR szDoc ) { // Reset indexes m_iPosFree = 1; ResetPos(); // Set document text if ( szDoc ) m_csDoc = szDoc; else m_csDoc.Empty(); // Starting size of position array: 1 element per 64 bytes of document // Tight fit when parsing small doc, only 0 to 2 reallocs when parsing large doc // Start at 8 when creating new document int nStartSize = m_csDoc.GetLength() / 64 + 8; if ( m_aPos.GetSize() < nStartSize ) m_aPos.SetSize( nStartSize ); // Parse document bool bWellFormed = false; if ( m_csDoc.GetLength() ) { m_aPos[0].Clear(); int iPos = x_ParseElem( 0 ); if ( iPos > 0 ) { m_aPos[0].iElemChild = iPos; bWellFormed = true; } } // Clear indexes if parse failed or empty document if ( ! bWellFormed ) { m_aPos[0].Clear(); m_iPosFree = 1; } ResetPos(); return bWellFormed; }; bool CMarkup::IsWellFormed() { if ( m_aPos.GetSize() && m_aPos[0].iElemChild ) return TRUE; return FALSE; } bool CMarkup::FindElem( LPCTSTR szName ) { // If szName is NULL or empty, go to next sibling element // Otherwise go to next sibling element with matching tag name // If the current position is valid, start looking from next // Change current position only if found // int iPos = m_iPos; if ( ! iPos ) { if ( m_aPos.GetSize() ) iPos = m_aPos[0].iElemChild; } else { iPos = m_aPos[iPos].iElemNext; } while ( iPos ) { // Compare tag name unless szName is not specified if ( szName == NULL || !szName[0] || x_GetTagName(iPos) == szName ) { // Assign new position m_iPos = iPos; m_iPosChild = 0; return true; } iPos = m_aPos[iPos].iElemNext; } return false; } bool CMarkup::FindChildElem( LPCTSTR szName ) { // If szName is NULL or empty, go to next sibling child element // Otherwise go to next sibling child element with matching tag name // If the current child position is valid, start looking from next // Change current child position only if found // // Shorthand: call this with no current position means under root element if ( ! m_iPos ) FindElem(); // Is main position valid and not empty? if ( ! m_iPos || m_aPos[m_iPos].IsEmptyElement() ) return false; // Is current child position valid? int iPosChild = m_iPosChild; if ( iPosChild ) iPosChild = m_aPos[iPosChild].iElemNext; else iPosChild = m_aPos[m_iPos].iElemChild; // Search while ( iPosChild ) { // Compare tag name unless szName is not specified if ( szName == NULL || !szName[0] || x_GetTagName(iPosChild) == szName ) { // Assign new position m_iPosChild = iPosChild; return true; } iPosChild = m_aPos[iPosChild].iElemNext; } return false; } bool CMarkup::IntoElem() { // Find child element unless there is already a child element position if ( ! m_iPosChild ) FindChildElem(); if ( m_iPosChild ) { m_iPos = m_iPosChild; m_iPosChild = 0; ++m_nLevel; return true; } return false; } bool CMarkup::OutOfElem() { // Go to parent element if ( m_iPos && m_nLevel > 0 ) { m_iPosChild = m_iPos; m_iPos = m_aPos[m_iPos].iElemParent; --m_nLevel; return true; } return false; } bool CMarkup::GetOffsets( int& nStart, int& nEnd ) const { // Return document offsets of current main position element // This is not part of EDOM but is used by the Markup project if ( m_iPos ) { nStart = m_aPos[m_iPos].nStartL; nEnd = m_aPos[m_iPos].nEndR; return true; } return false; } bool CMarkup::AddElem( LPCTSTR szName, LPCTSTR szValue ) { // Add an element after current main position int iPosParent = m_iPos? m_aPos[m_iPos].iElemParent : 0; m_iPosChild = 0; // Setting root element? if ( iPosParent == 0 ) { if ( IsWellFormed() ) return false; m_csDoc.Empty(); } m_iPos = x_Add( iPosParent, m_iPos, szName, szValue ); return true; } bool CMarkup::AddChildElem( LPCTSTR szName, LPCTSTR szValue ) { // Add a child element under main position, after current child position if ( ! m_iPos ) return false; // If no child position, add after last sibling int iPosLast = m_aPos[m_iPos].iElemChild; if ( ! m_iPosChild && iPosLast ) { m_iPosChild = iPosLast; while ( (iPosLast=m_aPos[iPosLast].iElemNext) != 0 ) m_iPosChild = iPosLast; } m_iPosChild = x_Add( m_iPos, m_iPosChild, szName, szValue ); return true; } bool CMarkup::AddAttrib( LPCTSTR szAttrib, LPCTSTR szValue ) { // Add attribute to current main position element if ( m_iPos ) { x_AddAttrib( m_iPos, szAttrib, szValue ); return true; } return false; } bool CMarkup::AddChildAttrib( LPCTSTR szAttrib, LPCTSTR szValue ) { // Add attribute to current child position element if ( m_iPosChild ) { x_AddAttrib( m_iPosChild, szAttrib, szValue ); return true; } return false; } ////////////////////////////////////////////////////////////////////// // Private Methods ////////////////////////////////////////////////////////////////////// int CMarkup::x_GetFreePos() { // // This returns the index of the next unused ElemPos in the array // if ( m_iPosFree == m_aPos.GetSize() ) m_aPos.SetSize( m_iPosFree + m_iPosFree / 2 ); ++m_iPosFree; return m_iPosFree - 1; } int CMarkup::x_ReleasePos() { // // This decrements the index of the next unused ElemPos in the array // allowing the element index returned by GetFreePos() to be reused // --m_iPosFree; return 0; } int CMarkup::x_ParseError( LPCTSTR szError, LPCTSTR szTag ) { if ( szTag ) m_csError.Format( szError, szTag ); else m_csError = szError; x_ReleasePos(); return -1; } int CMarkup::x_ParseElem( int iPosParent ) { // This is either called by SetDoc, x_AddSubDoc, or itself recursively // m_aPos[iPosParent].nEndL is where to start parsing for the child element // This returns the new position if a tag is found, otherwise zero // In all cases we need to get a new ElemPos, but release it if unused // int iPos = x_GetFreePos(); m_aPos[iPos].nStartL = m_aPos[iPosParent].nEndL; m_aPos[iPos].nNext = m_aPos[iPosParent].nStartR + 1; m_aPos[iPos].iElemParent = iPosParent; m_aPos[iPos].iElemChild = 0; m_aPos[iPos].iElemNext = 0; // Start Tag // A loop is used to ignore all remarks tags and special tags // i.e. <?xml version="1.0"?>, and <!-- comment here --> // So any tag beginning with ? or ! is ignored // Loop past ignored tags TokenPos token; token.nNext = m_aPos[iPosParent].nEndL; CString csName; while ( csName.IsEmpty() ) { // Look for left angle bracket of start tag m_aPos[iPos].nStartL = token.nNext; if ( ! x_FindChar( m_aPos[iPos].nStartL, _T('<') ) ) return x_ParseError( _T("Element tag not found") ); // Set parent's End tag to start looking from here (or later) m_aPos[iPosParent].nEndL = m_aPos[iPos].nStartL; // Determine whether this is an element, comment or version tag LPCTSTR szEndOfTag = NULL; token.nNext = m_aPos[iPos].nStartL + 1; if ( x_FindToken( token ) ) { if ( token.bIsString ) return x_ParseError( _T("Tag starts with quote") ); TCHAR cFirstChar = m_csDoc[token.nL]; if ( cFirstChar == _T('?') ) szEndOfTag = _T("?>"); // version else if ( cFirstChar == _T('!') ) { TCHAR cSecondChar = 0; if ( token.nL+1 < m_csDoc.GetLength() ) cSecondChar = m_csDoc[token.nL+1]; if ( cSecondChar == _T('[') ) szEndOfTag = _T("]]>"); // CDATA section else if ( cSecondChar == _T('-') ) szEndOfTag = _T("-->"); // comment else szEndOfTag = _T(">"); // DTD } else if ( cFirstChar != _T('/') ) { csName = x_GetToken( token ); szEndOfTag = _T(">"); } else return x_ReleasePos(); // probably end tag of parent } else return x_ParseError( _T("Abrupt end within tag") ); // Look for end of tag token.nNext = m_csDoc.Find( szEndOfTag, token.nNext ); if ( token.nNext == -1 ) return x_ParseError( _T("End of tag not found") ); } m_aPos[iPos].nStartR = token.nNext; // Is ending mark within start tag, i.e. empty element? if ( m_csDoc[m_aPos[iPos].nStartR-1] == _T('/') ) { // Empty element // Close tag left is set to ending mark, and right to open tag right m_aPos[iPos].nEndL = m_aPos[iPos].nStartR-1; m_aPos[iPos].nEndR = m_aPos[iPos].nStartR; } else // look for end tag { // Element probably has contents // Determine where to start looking for left angle bracket of end tag // This is done by recursively parsing the contents of this element int iInner, iInnerPrev = 0; m_aPos[iPos].nEndL = m_aPos[iPos].nStartR + 1; while ( (iInner = x_ParseElem( iPos )) > 0 ) { // Set links to iInner if ( iInnerPrev ) m_aPos[iInnerPrev].iElemNext = iInner; else m_aPos[iPos].iElemChild = iInner; iInnerPrev = iInner; // Set offset to reflect child m_aPos[iPos].nEndL = m_aPos[iInner].nEndR + 1; } if ( iInner == -1 ) return -1; // Look for left angle bracket of end tag if ( ! x_FindChar( m_aPos[iPos].nEndL, _T('<') ) ) return x_ParseError( _T("End tag of %s element not found"), csName ); // Look through tokens of end tag token.nNext = m_aPos[iPos].nEndL + 1; int nTokenCount = 0; while ( x_FindToken( token ) ) { ++nTokenCount; if ( ! token.bIsString ) { // Is first token not an end slash mark? if ( nTokenCount == 1 && m_csDoc[token.nL] != _T('/') ) return x_ParseError( _T("Expecting end tag of element %s"), csName ); else if ( nTokenCount == 2 && csName != x_GetToken( token ) ) return x_ParseError( _T("End tag does not correspond to %s"), csName ); // Else is it a right angle bracket? else if ( m_csDoc[token.nL] == _T('>') ) break; } } // Was a right angle bracket not found? if ( ! token.IsValid() || nTokenCount < 2 ) return x_ParseError( _T("End tag not completed for element %s"), csName ); m_aPos[iPos].nEndR = token.nL; } // Successfully found positions of angle brackets m_aPos[iPos].nNext = m_aPos[iPos].nEndR; x_FindChar( m_aPos[iPos].nNext, _T('<') ); return iPos; } bool CMarkup::x_FindChar( int&n, _TCHAR c ) const { // Look for char c starting at n, and set n to point to it // c is always the first char of a multi-byte char // Return false if not found before end of document LPCTSTR szDoc = (LPCTSTR)m_csDoc; while ( szDoc[n] && szDoc[n] != c ) n += _tclen( &szDoc[n] ); if ( ! szDoc[n] ) return false; return true; } bool CMarkup::x_FindToken( CMarkup::TokenPos& token ) const { // Starting at token.nNext, find the next token // upon successful return, token.nNext points after the retrieved token LPCTSTR szDoc = (LPCTSTR)m_csDoc; int n = token.nNext; // Statically defined CStrings for whitespace and special chars static CString csWhitespace = _T(" /t/n/r"); static CString csSpecial = _T("<>=///?!"); // By-pass leading whitespace while ( szDoc[n] && csWhitespace.Find(szDoc[n]) > -1 ) ++n; // Are we still within the document? token.bIsString = false; if ( szDoc[n] ) { // Is it an opening quote? if ( szDoc[n] == _T('/"') ) { // Move past opening quote ++n; token.nL = n; // Look for closing quote x_FindChar( n, _T('/"') ); // Set right to before closing quote token.nR = n-1; // Set n past closing quote unless at end of document if ( szDoc[n] ) ++n; // Set flag token.bIsString = true; } else { // Go until special char or whitespace token.nL = n; while ( szDoc[n] && csSpecial.Find(m_csDoc[n]) == -1 && csWhitespace.Find(m_csDoc[n]) == -1 ) n += _tclen(&szDoc[n]); // Adjust end position if it is one special char if ( n == token.nL ) ++n; // it is a special char token.nR = n-1; } } token.nNext = n; if ( ! szDoc[n] ) return false; // nNext points to one past last char of token return true; } CString CMarkup::x_GetToken( const CMarkup::TokenPos& token ) const { // The token contains indexes into the document identifying a small substring // Build the substring from those indexes and return it if ( ! token.IsValid() ) return _T(""); return m_csDoc.Mid( token.nL, token.nR - token.nL + ((token.nR<m_csDoc.GetLength())? 1:0) ); } CString CMarkup::x_GetTagName( int iPos ) const { // Return the tag name at specified element TokenPos token; token.nNext = m_aPos[iPos].nStartL + 1; if ( ! iPos || ! x_FindToken( token ) ) return _T(""); // Return substring of document return x_GetToken( token ); } bool CMarkup::x_FindAttrib( CMarkup::TokenPos& token, LPCTSTR szAttrib ) const { // If szAttrib is NULL find next attrib, otherwise find named attrib // Return true if found int nAttrib = 0; for ( int nCount = 0; x_FindToken(token); ++nCount ) { if ( ! token.bIsString ) { // Is it the right angle bracket? if ( m_csDoc[token.nL] == _T('>') || m_csDoc[token.nL] == _T('/') ) break; // attrib not found // Equal sign if ( m_csDoc[token.nL] == _T('=') ) continue; // Potential attribute if ( ! nAttrib && nCount ) { // Attribute name search? if ( ! szAttrib || ! szAttrib[0] ) return true; // return with token at attrib name // Compare szAttrib if ( x_GetToken(token) == szAttrib ) nAttrib = nCount; } } else if ( nAttrib && nCount == nAttrib + 2 ) { return true; } } // Not found return false; } CString CMarkup::x_GetAttrib( int iPos, LPCTSTR szAttrib ) const { // Return the value of the attrib at specified element TokenPos token; token.nNext = m_aPos[iPos].nStartL + 1; if ( szAttrib && x_FindAttrib( token, szAttrib ) ) return x_TextFromDoc( token.nL, token.nR - ((token.nR<m_csDoc.GetLength())?0:1) ); return _T(""); } CString CMarkup::x_GetData( int iPos ) const { // Return a string representing data between start and end tag // Return empty string if there are any children elements if ( ! m_aPos[iPos].iElemChild && ! m_aPos[iPos].IsEmptyElement() ) { // See if it is a CDATA section TokenPos token; token.nNext = m_aPos[iPos].nStartR+1; if ( x_FindToken( token ) && m_csDoc[token.nL] == _T('<') && token.nL + 11 < m_aPos[iPos].nEndL && _tcsncmp( &((LPCTSTR)m_csDoc)[token.nL+1], _T("![CDATA["), 8 ) == 0 ) { int nEndCDATA = m_csDoc.Find( _T("]]>"), token.nNext ); if ( nEndCDATA != -1 && nEndCDATA < m_aPos[iPos].nEndL ) { return m_csDoc.Mid( token.nL+9, nEndCDATA-token.nL-9 ); } } return x_TextFromDoc( m_aPos[iPos].nStartR+1, m_aPos[iPos].nEndL-1 ); } return ""; } CString CMarkup::x_TextToDoc( LPCTSTR szText, bool bAttrib ) const { // Convert text as seen outside XML document to XML friendly // replacing special characters with ampersand escape codes // E.g. convert "6>7" to "6>7" // // < less than // & ampersand // > greater than // // and for attributes: // // ' apostrophe or single quote // " double quote // static _TCHAR* szaReplace[] = { _T("<"),_T("&"),_T(">"),_T("'"),_T(""") }; const _TCHAR* pFind = bAttrib?_T("<&>/'/""):_T("<&>"); CString csText; const _TCHAR* pSource = szText; int nDestSize = _tcslen(pSource); nDestSize += nDestSize / 10 + 7; _TCHAR* pDest = csText.GetBuffer(nDestSize); int nLen = 0; _TCHAR cSource = *pSource; _TCHAR* pFound; while ( cSource ) { if ( nLen > nDestSize - 6 ) { csText.ReleaseBuffer(nLen); nDestSize *= 2; pDest = csText.GetBuffer(nDestSize); } if ( (pFound=_tcschr(pFind,cSource)) != NULL ) { pFound = szaReplace[pFound-pFind]; _tcscpy(&pDest[nLen],pFound); nLen += _tcslen(pFound); } else { _tccpy( &pDest[nLen], pSource ); ++nLen; } pSource += _tclen( pSource ); cSource = *pSource; } csText.ReleaseBuffer(nLen); return csText; } CString CMarkup::x_TextFromDoc( int nLeft, int nRight ) const { // Convert XML friendly text to text as seen outside XML document // replacing ampersand escape codes with special characters // E.g. convert "6>7" to "6>7" // // Conveniently the result is always the same or shorter in length // static _TCHAR* szaCode[] = { _T("lt;"),_T("amp;"),_T("gt;"),_T("apos;"),_T("quot;") }; static int anCodeLen[] = { 3,4,3,5,5 }; static _TCHAR* szSymbol = _T("<&>/'/""); CString csText; const _TCHAR* pSource = m_csDoc; int nDestSize = nRight - nLeft + 1; _TCHAR* pDest = csText.GetBuffer(nDestSize); int nLen = 0; int nCharLen; int nChar = nLeft; while ( nChar <= nRight ) { if ( pSource[nChar] == _T('&') ) { // Look for matching &code; for ( int nMatch = 0; nMatch < 5; ++nMatch ) { if ( nChar <= nRight - anCodeLen[nMatch] && _tcsncmp(szaCode[nMatch],&pSource[nChar+1],anCodeLen[nMatch]) == 0 ) { pDest[nLen++] = szSymbol[nMatch]; nChar += anCodeLen[nMatch] + 1; break; } } // If no match is found it means XML doc is invalid // no devastating harm done, ampersand code will just be left in result if ( nMatch == 5 ) { pDest[nLen++] = _T('&'); ++nChar; } } else { nCharLen = _tclen(&pSource[nChar]); _tccpy( &pDest[nLen], &pSource[nChar] ); nLen += nCharLen; nChar += nCharLen; } } csText.ReleaseBuffer(nLen); return csText; } void CMarkup::x_DocChange( int nLeft, int nReplace, const CString& csInsert ) { // Insert csInsert int m_csDoc at nLeft replacing nReplace chars // Do this with only one buffer reallocation if it grows // int nDocLength = m_csDoc.GetLength(); int nInsLength = csInsert.GetLength(); // Make sure nLeft and nReplace are within bounds nLeft = max( 0, min( nLeft, nDocLength ) ); nReplace = max( 0, min( nReplace, nDocLength-nLeft ) ); // Get pointer to buffer with enough room int nNewLength = nInsLength + nDocLength - nReplace; int nBufferLen = nNewLength; _TCHAR* pDoc = m_csDoc.GetBuffer( nBufferLen ); // Move part of old doc that goes after insert if ( nLeft+nReplace < nDocLength ) memmove( &pDoc[nLeft+nInsLength], &pDoc[nLeft+nReplace], (nDocLength-nLeft-nReplace)*sizeof(_TCHAR) ); // Copy insert memcpy( &pDoc[nLeft], csInsert, nInsLength*sizeof(_TCHAR) ); // Release m_csDoc.ReleaseBuffer( nNewLength ); } void CMarkup::x_Adjust( int iPos, int nShift ) { // Loop through affected elements and adjust indexes // Does not affect iPos itself // Algorithm: // 1. update next siblings and all their children // 2. then go up a level update end points and to step 1 int iPosTop = m_aPos[iPos].iElemParent; while ( iPos ) { // Were we at containing parent of affected position? bool bPosTop = false; if ( iPos == iPosTop ) { // Move iPosTop up one towards root iPosTop = m_aPos[iPos].iElemParent; bPosTop = true; } // Traverse to the next update position if ( ! bPosTop && m_aPos[iPos].iElemChild ) { // Depth first iPos = m_aPos[iPos].iElemChild; } else if ( m_aPos[iPos].iElemNext ) { iPos = m_aPos[iPos].iElemNext; } else { // Look for next sibling of a parent of iPos // When going back up, parents have already been done except iPosTop while ( (iPos=m_aPos[iPos].iElemParent) != 0 && iPos != iPosTop ) if ( m_aPos[iPos].iElemNext ) { iPos = m_aPos[iPos].iElemNext; break; } } // Shift indexes at iPos if ( iPos != iPosTop ) { // Move the start tag indexes // Don't do this for containing parent tag m_aPos[iPos].nStartL += nShift; m_aPos[iPos].nStartR += nShift; } // Move end tag indexes m_aPos[iPos].nEndL += nShift; m_aPos[iPos].nEndR += nShift; m_aPos[iPos].nNext += nShift; } } int CMarkup::x_Add( int iPosParent, int iPosBefore, LPCTSTR szName, LPCTSTR szValue ) { // Create element and modify positions of affected elements // if iPosBefore is NULL, insert as first element under parent // If no szValue is specified, an empty element is created // i.e. either <NAME>value</NAME> or <NAME/> // int iPos = x_GetFreePos(); bool bEmptyParent = false; if ( iPosBefore ) { // Follow iPosBefore m_aPos[iPos].nStartL = m_aPos[iPosBefore].nNext; } else if ( m_aPos[iPosParent].iElemChild ) { // Insert before first child of parent m_aPos[iPos].nStartL = m_aPos[m_aPos[iPosParent].iElemChild].nStartL; } else if ( m_aPos[iPosParent].IsEmptyElement() ) { // Parent has no separate end tag m_aPos[iPos].nStartL = m_aPos[iPosParent].nStartR + 2; bEmptyParent = true; } else { // Parent has content, but no children m_aPos[iPos].nStartL = m_aPos[iPosParent].nEndL; } // Set links m_aPos[iPos].iElemParent = iPosParent; m_aPos[iPos].iElemChild = 0; if ( iPosBefore ) { m_aPos[iPos].iElemNext = m_aPos[iPosBefore].iElemNext; m_aPos[iPosBefore].iElemNext = iPos; } else { m_aPos[iPos].iElemNext = m_aPos[iPosParent].iElemChild; m_aPos[iPosParent].iElemChild = iPos; } // Create string for insert CString csInsert; int nLenName = _tcslen(szName); int nLenValue = szValue? _tcslen(szValue) : 0; if ( ! nLenValue ) { // <NAME/> empty element csInsert.Format( _T("

This article has been re-written with the help of 2 years of feedback, and the new source code has benefited from all of the fixes and developments during that time period. See release notes below.

Introduction

Often times you don't want to invest in learning a complex XML tool to implement a little bit of XML processing in your application. Its SO Easy! Just add Markup.cpp and Markup.h to your Visual C++ MFC project, #include "Markup.h", and begin using it. There are no other dependencies.

Features

  • Light: one small class that maintains one single document string with a simple array of indexes
  • Fast: the parser builds the index array in one quick pass
  • Simple: EDOM methods make it ridiculously easy to create or process XML strings
  • Independent: compiles into your program without requiring MSXML or any tokenizer
  • UNICODE: can be compiled for UNICODE for Windows CE and NT/XP platforms (define _UNICODE)
  • UTF-8: when not in UNICODE or MBCS builds, it works with UTF-8, ASCII, or Windows extended sets
  • MBCS: can be compiled for Windows double-byte character sets such as Chinese GB2312 (define _MBCS)

XML for Everyday Data

We often need to store and/or pass information in a file, or send a block of information from computer A to computer B. And the issue is always the same: How shall I format this data? Before XML, you might have considered "env" style e.g. PATH=C:/WIN95; "ini" style (grouped in sections); comma-delimited or otherwise delimited; or fixed character lengths. XML is now the established answer to that question except that programmers are sometimes discouraged by the size and complexity of XML solutions when all they need is something convenient to help parse and format angle brackets. For good minimalist reading on the syntax rules for XML tags, I recommend Beginning XML - Chapter 2: Well-Formed XML posted here on the Code Project.

XML is better because of its flexible and hierarchical nature, plus its wide acceptance. Although XML uses more characters than delimited formats, it compresses down well if needed. The flexibility of XML becomes apparent when you want to expand the types of information your document can contain without requiring every consumer of the information to rewrite processing logic. You can keep the old information identified and ordered the same way it was while adding new attributes and elements.

CMarkup Lite Methods

CMarkup is based on the "Encapsulated" Document Object Model (EDOM), the key to simple XML processing. Its a set of methods for XML processing with the same general purpose as DOM (Document Object Model). But while DOM has numerous types of objects, EDOM defines only one object, the XML document. EDOM harks back to the original attraction of XML which was its simplicity. To keep overhead low, CMarkup takes a very light non-conforming non-validating approach to XML, and it does not verify the XML is well-formed.

The CMarkup "Lite" in this article is the free version of the CMarkup product sold at firstobject.com. CMarkup Lite implements a subset of EDOM methods for creating and parsing XML document strings. The Lite methods also encompass some modification functionality such as setting an attribute or adding additional elements to an existing XML document, but not changing the data of, or removing, XML elements. See the EDOM specification to compare the full CMarkup with CMarkup Lite. The full CMarkup is available in Evaluation (Educational) and licensed Developer versions with many more methods, STL and MSXML versions, Base64, and additional documentation. But this Lite version here at Code Project is more than adequate for parsing and creating simple XML strings in MFC.

The CMarkup Lite methods are grouped into Creation and Navigation categories listed below.

CMarkup Lite Creation Methods

Collapse Copy Code
CString GetDoc() const { return m_csDoc; };
bool AddElem( LPCTSTR szName, LPCTSTR szData=NULL );
bool AddChildElem( LPCTSTR szName, LPCTSTR szData=NULL );
bool AddAttrib( LPCTSTR szAttrib, LPCTSTR szValue );
bool AddChildAttrib( LPCTSTR szAttrib, LPCTSTR szValue );
bool SetAttrib( LPCTSTR szAttrib, LPCTSTR szValue );
bool SetChildAttrib( LPCTSTR szAttrib, LPCTSTR szValue );

GetDoc is used to get the document string after adding elements and setting attributes. The AddAttrib and SetAttrib methods do the same thing as each other (as do AddChildAttrib and SetChildAttrib). They will change the attribute's value if it already exists, and add the attribute if it doesn't.

CMarkup Lite Navigation Methods

Collapse Copy Code
bool SetDoc( LPCTSTR szDoc );
bool IsWellFormed();
bool FindElem( LPCTSTR szName=NULL );
bool FindChildElem( LPCTSTR szName=NULL );
bool IntoElem();
bool OutOfElem();
void ResetChildPos();
void ResetMainPos();
void ResetPos();
CString GetTagName() const;
CString GetChildTagName() const;
CString GetData() const;
CString GetChildData() const;
CString GetAttrib( LPCTSTR szAttrib ) const;
CString GetChildAttrib( LPCTSTR szAttrib ) const;
CString GetError() const;

When you call SetDoc it parses the szDoc string and populates the CMarkup object. If it fails, it returns false, and you can call GetError for an error description. The IsWellFormed method returns true if the CMarkup object has at least a root element; it does not verify well-formedness.

Using CMarkup

The CMarkup class encapsulates the XML document text, structure, and current positions. It has methods both to add elements and to navigate and get element attributes and data. The locations in the document where operations are performed are governed by the current position and the current child position. This current positioning allows you to work with the XML document without instantiating additional objects that point into the document. At all times, the object maintains a string representing the text of the document which can be retrieved using GetDoc.

Check out the free firstobject XML editor which generates C++ source code for creating and navigating your own XML documents with CMarkup Lite.

Creating an XML Document

To create an XML document, instantiate a CMarkup object and call AddElem to create the root element. At this point, if you called AddElem("ORDER") your document would simply contain the empty ORDER element <ORDER/>. Then call AddChildElem to create elements under the root element (i.e. "inside" the root element, hierarchically speaking). The following example code creates an XML document and retrieves it into a CString:

Collapse Copy Code
CMarkup xml;
xml.AddElem( "ORDER" );
xml.AddChildElem( "ITEM" );
xml.IntoElem();
xml.AddChildElem( "SN", "132487A-J" );
xml.AddChildElem( "NAME", "crank casing" );
xml.AddChildElem( "QTY", "1" );
CString csXML = xml.GetDoc();

This code generates the following XML. The root is the ORDER element; notice that its start tag <ORDER> is at the beginning and end tag </ORDER> is at the bottom. When an element is under (i.e. inside or contained by) a parent element, the parent's start tag is before it and the parent's end tag is after it. The ORDER element contains one ITEM element. That ITEM element contains 3 child elements: SN, NAME, and QTY.

Collapse Copy Code
<ORDER>
<ITEM>
<SN>132487A-J</SN>
<NAME>crank casing</NAME>
<QTY>1</QTY>
</ITEM>
</ORDER>

As shown in the example, you can create elements under a child element by calling IntoElem to move your current main position to where the current child position is so you can begin adding under what was the child element. CMarkup maintains a current position in order to keep your source code shorter and simpler. This same position logic is used when navigating a document.

Navigating an XML Document

The XML string created in the above example can be parsed into a CMarkup object with the SetDoc method. You can also navigate it right inside the same CMarkup object where it was created; just call ResetPos if you want to reset the current position back to the beginning of the document.

In the following example, after populating the CMarkup object from the csDoc string, we loop through all ITEM elements under the ORDER element and get the serial number and quantity of each item:

Collapse Copy Code
CMarkup xml;
xml.SetDoc( csXML );
while ( xml.FindChildElem("ITEM") )
{
    xml.IntoElem();
    xml.FindChildElem( "SN" );
    CString csSN = xml.GetChildData();
    xml.FindChildElem( "QTY" );
    int nQty = atoi( xml.GetChildData() );
    xml.OutOfElem();
}

For each item we find, we call IntoElem before interrogating its child elements, and then OutOfElem afterwards. As you get accustomed to this type of navigation you will know to check in your loops to make sure there is a corresponding OutOfElem call for every IntoElem call.

Adding Elements and Attributes

The above example for creating a document only created one ITEM element. Here is an example that creates multiple items loaded from a previously populated data source, plus a SHIPMENT information element in which one of the elements has an attribute. This code also demonstrates that instead of calling AddChildElem, you can call IntoElem and AddElem. It means more calls, but some people find this more intuitive.

Collapse Copy Code
CMarkup xml;
xml.AddElem( "ORDER" );
xml.IntoElem(); // inside ORDER
for ( int nItem=0; nItem<aItems.GetSize(); ++nItem )
{
    xml.AddElem( "ITEM" );
    xml.IntoElem(); // inside ITEM
    xml.AddElem( "SN", aItems[nItem].csSN );
    xml.AddElem( "NAME", aItems[nItem].csName );
    xml.AddElem( "QTY", aItems[nItem].nQty );
    xml.OutOfElem(); // back out to ITEM level
}
xml.AddElem( "SHIPMENT" );
xml.IntoElem(); // inside SHIPMENT
xml.AddElem( "POC" );
xml.SetAttrib( "type", csPOCType );
xml.IntoElem(); // inside POC
xml.AddElem( "NAME", csPOCName );
xml.AddElem( "TEL", csPOCTel );

This code generates the following XML. The root ORDER element contains 2 ITEM elements and a SHIPMENT element. The ITEM elements both contain SN, NAME and QTY elements. The SHIPMENT element contains a POC element which has a type attribute, and NAME and TEL child elements.

Collapse Copy Code
<ORDER>
<ITEM>
<SN>132487A-J</SN>
<NAME>crank casing</NAME>
<QTY>1</QTY>
</ITEM>
<ITEM>
<SN>4238764-A</SN>
<NAME>bearing</NAME>
<QTY>15</QTY>
</ITEM>
<SHIPMENT>
<POC type="non-emergency">
<NAME>John Smith</NAME>
<TEL>555-1234</TEL>
</POC>
</SHIPMENT>
</ORDER>

Finding Elements

The FindElem and FindChildElem methods go to the next sibling element. If the optional tag name argument is specified, then they go to the next element with a matching tag name. The element that is found becomes the current element, and the next call to Find will go to the next sibling or matching sibling after that current position.

When you cannot assume the order of the elements, you must reset the position in between calling the Find method. Looking at the ITEM element in the above example, if someone else is creating the XML and you cannot assume the SN element is before the QTY element, then call ResetChildPos() before finding the QTY element.

To find the item with a particular serial number, you can loop through the ITEM elements and compare the SN element data to the serial number you are searching for. This example differs from the original navigation example by calling IntoElem to go into the ORDER element and use FindElem("ITEM") instead of FindChildElem("ITEM"); either way is fine. And notice that by specifying the "ITEM" element tag name in the Find method we ignore all other sibling elements such as the SHIPMENT element.

Collapse Copy Code
CMarkup xml;
xml.SetDoc( csXML );
xml.FindElem(); // ORDER element is root
xml.IntoElem(); // inside ORDER
while ( xml.FindElem("ITEM") )
{
    xml.FindChildElem( "SN" );
    if ( xml.GetChildData() == csFindSN )
        break; // found
}

Encodings

ASCII refers to the character codes under 128 that we have come to depend on, programming in English. Conveniently if you are only using ASCII, UTF-8 encoding is the same as your common ASCII set.

If you are using a character set not corresponding to one of the Unicode sets UTF-8, UTF-16 or UCS-2, you really should declare it in your XML declaration for the sake of interoperability and viewing it properly in Internet Explorer. Character sets like ISO-8859-1 (Western European) assign characters to the values in a byte between 128 and 255, so that every character still only uses one byte. Windows double-byte character sets such as GB2312, Shift_JIS and EUC-KR use one or two bytes per character. For these Windows charsets, put _MBCS in your preprocessor definitions and make sure your user's Operating System is set to the corresponding code page.

To prefix your XML document with an XML declaration such as <?xml version="1.0" encoding="ISO-8859-1"?>, pass it to SetDoc or the CMarkup constructor. Include a CRLF at the end as shown so that the root element goes on the next line.

Collapse Copy Code
xml.SetDoc( "<?xml version=/"1.0/" encoding=/"ISO-8859-1/"?>/r/n" );
xml.AddElem( "island", "Cura�ao" );

Depth First Traversal

You can use the following code to loop through every element in your XML document. In the part of the code where you process the element, every element in the document (except the root element) will be encountered in depth first order. For illustrative purposes, it gets the tag name of the element. If you were searching for a particular element tag name you could break out of the loop at this point. "Depth first" means that it traverses all of an element's children before going to its sibling.

Collapse Copy Code
BOOL bFinished = FALSE;
xml.ResetPos();
if ( ! xml.FindChildElem() )
    bFinished = TRUE;
while ( ! bFinished )
{
    // Process element
    xml.IntoElem();
    CString csTag = xml.GetTagName();

    // Next element (depth first)
    BOOL bFound = xml.FindChildElem();
    while ( ! bFound && ! bFinished )
    {
        if ( xml.OutOfElem() )
            bFound = xml.FindChildElem();
        else
            bFinished = TRUE;
    }
}

Loading and Saving Files

CMarkup Lite does not have Load and Save methods. To load a file, look in the CMarkupDlg::OnButtonParse method which loads a file into a string. Once you have it in a string, you can put it into the CMarkup object using SetDoc. To save it to a file, call GetDoc to get the string and then implement your own code to write the string to your file. When you need to implement any of your own project specific I/O error handling, streaming, permissions/locking, and charset conversion, it is actually good software design to keep this outside of the CMarkup class allowing CMarkup to remain a generic class.

The Test Dialog

The Markup.exe test bed for CMarkup is a Visual Studio 6.0 MFC project (also compiles in VS .NET too). When the dialog starts, it performs diagnostics in the RunTest function to test CMarkup in the context of the particular build options that have been selected. You can step through the RunTest function to see a lot of examples of how to use CMarkup. Use the Open and Parse button in the dialog to test a file.

In the following illustration, the Build Version is shown as "CMarkup Lite 6.5 Debug Unicode." This means that it is the debug version built with _UNICODE defined. The RunTest completed successfully. A parse error was encountered in the order_e.xml file. It also shows the load and parse times, and file size.

The Test Dialog keeps track of the last file parsed and the dialog screen position for convenience. This is kept in the registry under HKEY_CURRENT_USER/ Software/ First Objective Software/ Markup/ Settings.

How CMarkup Works

The CMarkup strategy is to leave the data in the document string and maintain a hierarchical arrangement of indexes mapping out the document.

  • increase speed: parse in one pass and maintain hierarchy of indexes
  • reduce overhead: do not copy or break up the text of the document

CMarkup parses the 250k play.xml sample document in about 40 milliseconds (1/25th of a second) on a 500Mhz machine, holding it as a single string, and allocating about 200k for a map of the 6343 elements. From then on, navigation does not require any parsing. As a rule of thumb, the map of indexes takes up approximately the same amount of memory as the document, so the memory footprint of the CMarkup object should settle down around 2 times the size of the document. For each element in the document a struct of eight integers (32 bytes) is maintained.

Collapse Copy Code
int nStartL;
int nStartR;
int nEndL;
int nEndR;
int nReserved;
int iElemParent;
int iElemChild;
int iElemNext;

Look at the start and end tags in <QTY>1</QTY>. The struct contains the offsets of the left and right of both the start and end tags (i.e. all the < and > signs). The reserved integer is not currently used but could be used for a delete flag and/or level (i.e. depth) in the hierarchy to support indentation. The other three integers are indexes to the structs for the parent, child and next elements.

When the document is first parsed an array of these structs is built, and then as elements are modified and inserted in the XML, the structs are modified and added. Rather than allocating structs individually, they are allocated in an array using a "grow-by" mechanism to reduce the number of allocations to a handful. That is why integer array indexes rather than pointers are used for the links. Once an element is assigned an index in the array, that index does not change. So the index can be used as a way of referring to and locating an element

Release Notes

This release 6.5 of CMarkup Lite's public methods are backwards compatible with the previous release 6.1 posted here in August 2001 except for one rare usage of IntoElem. In 6.1, if you called IntoElem without a current child element, it would find the first child element. Now in 6.5 when there is no current child position, IntoElem puts the main position before the first child element so that a subsequent call to FindElem will not bypass the first element. So, the quick way to check this when upgrading is to scan all occurrences of IntoElem and make sure the previous CMarkup navigation call is FindChildElem before it. Or, if the child element was just created with AddChildElem then its okay because that sets the current child position too. For full details on this, see the IntoElem Changes in Release 6.3.

Other major changes since 6.1:

  • Fix: MBCS double-byte text x_TextToDoc *thanks knight_zhuge
  • Performance: parsing is roughly twice as fast
  • Debugging: see m_pMainDS and m_pChildDS class members while debugging to see string pointers showing current main and child positions
  • New Test Dialog interface with diagnostic results and load vs. parse times, and RunTest code for startup

License

CMarkup Lite is free for compiling into your commercial, personal and educational applications. Modify it as much as you like, but retain the copyright notice in the source code remarks. Redistribution of the modified or unmodified CMarkup Lite class source code is limited to your own development team and it cannot be made publicly available or distributable as part of any source code library or product, even if that offering is free. For source code products that derive from or utilize CMarkup Lite, please refer users to this article to obtain the source files for themselves. You are encouraged to discuss this source code and share enhancements here in the discussion board under this article. Enjoy!

<!-- Main Page Contents End -->

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Ben Bryant


Member
Raised in Southern Ontario Canada. Bachelor of Science from the University of Toronto in Computer Science and Anthropology. Living near Washington D.C. in Virginia, USA.
Location: United States United States
from http://www.codeproject.com/KB/cpp/markupclass.aspx
C++机器人开发:yaml-cpp配置文件实战与ROS参数管理 在软件工程中,配置文件是实现参数化设计、提升系统可维护性的核心机制。其原理是将易变的运行参数从固定的程序逻辑中解耦,通过外部文件进行管理,从而实现无需重新编译即可调整程序行为的技术价值。这一设计在机器人开发领域尤为重要,因为机器人系统通常涉及大量需要现场调试的参数,如PID控制系数、传感器标定数据和导航算法阈值。通过采用人类可读性强的YAML格式,结合成熟的C++解析库yaml-cpp,开发者可以构建出支持动态重载、分层管理的健壮配置系统。本文以机器人开发为应用场景,深入探讨了如何利用yaml-cpp进行高 阅读详情

相关推荐

轻量级C++ XML工具库markup详解与实战

在现代C++项目中,良好的头文件设计不仅决定了库的易用性,更直接影响其可维护性和扩展潜力。Markup.h作为markup库的核心接口声明文件,承载了整个XML解析系统对外暴露的所有功能入口。该头文件通过清晰的类层次划分、合理的函数命名规范以及对标准C++特性的恰当运用,构建了一个简洁而强大的API体系。深入剖析这一头文件的设计逻辑,有助于开发者理解如何高效地调用库功能,并为后续阅读实现代码(如Markup.cpp)提供结构化视角。

weixin_42514540的博客 895

Markup.h和Markup.cpp Release 11.5

// Markup.h: interface for the CMarkup class. // // Markup Release 11.5 // Copyright (C) 2011 First Objective Software, Inc. All rights reserved // Go to www.firstobject.com for the latest CMarkup and

Nero Zhang的博客 1767

Markup.h和cpp读取xml

添加到自己的工程中,可以用来读取XML中的信息,比较方便无须编译其他库

C++下利用CMarkup来解析xml文件46

昨天要实现一个从xml文件中读取摄像头IP和ID的功能,再晚上搜索了下,有CMarkup和TinyXml等相应的类库,就选择了CMarkup。 第一步 配置CMarkup   网址贴上:CMarkup,点击下载即可。下载完毕后,将其中的CMarkup.h添加到工程中的附加库中去,再将CMarkup.cpp添加到你的项目中,然后在main.cpp中包含头文件#include”CMarkup.h"

cwt19902010的专栏 2699

Markup(处理vc中XML类库)

c++中用来解析xml文件,解压后里面是一个DEMO,将Markup.h .cpp拷贝并添加到工程中可以使用,有官网下载。

大话西游2经典单机版V8:一键架设+GM全功能+仙玉自由修改

本文详细介绍大话西游2经典单机版V8的一键架设教程和GM全功能使用指南,包括仙玉自由修改技巧。该版本无需虚拟机,解压即玩,完美还原官服经典玩法,支持离线运行,是老玩家重温经典和新玩家体验游戏的理想选择。

weixin_27303545的博客 1131

C++使用Markup解析xml文件

http://www.firstobject.com/dn_markup.htm

hallyz的博客 1103

对E-R模型向关系模式转换的认识

一:基本E-R图的转换 1     属性处理 派生属性:直接忽略。 复合属性:展平技术。 多值属性:单独建立一个关系表,关系名就是多值属性名,如果是实体集的多值属性,关系的属性就是实体集的码加上多值属性,如果是联系集的多值属性,关系的属性就是各个实体集的码加上多值属性,关系的码要具体情况具体分析。 2     基本E-R图转换为关系模式 强实体集:一个关系,关系名就是强实体集名,关系属

优快云_LYY的专栏 2731

使用Markup解析xml字符串

参考博客:https://www.cnblogs.com/cxq0017/p/6553466.html https://www.cnblogs.com/ultimateWorld/p/6072820.html 项目中需要用到xml字符串的解析,上网看了一些工具, 觉得markup比较适合我,下面简单介绍markup解析xml格式字符串。 下载 http://www.firstobject....

Hyl的博客 1396

Vue集成飞书前端JSSDK

文章目录前言<1>飞书前端JSSDK<2>使用步骤1.将JSSDK引入到VUE2.读入数据总结 前言 公司需要使用飞书软件,所以转战飞书开发,也是对新知识的探索与研究,飞书SDK也一直都在更新,路虽远,行必达。 <1>飞书前端JSSDK 1、官网地址:https://open.feishu.cn/document/uYjL24iN/uITO4IjLykDOy4iM5gjM 2、当前使用的飞书前端JSSDk的版本:1.5.4 <!-- 在html文档中 --&gt

A 1万+

深入解析数据通信中的码元速率与比特率:从基础到实战应用

本文深入解析了数据通信中的核心概念码元速率与比特率,通过生动的比喻和公式(比特率=波特率×log₂(M))揭示其本质关系。文章结合BPSK、QPSK、16QAM等调制技术,剖析了从4G/5G到LoRa等实战场景中如何权衡速率、距离与可靠性,并提供了软考真题解析与系统设计建议,帮助读者从理论到应用全面掌握数据速率优化。

weixin_29324877的博客 213

VC6.0 添加CMarkup文件,程序编译不成功

其次看图标Markup.cpp 与其他图标存在不一样:Project ->Setting ->选择对应的文件 ->取消勾选Exclude file from build就欧克了!首先将文件添加进工程,右键选择 Add Files to Project…,选择对应的文件Markup.cpp, Markup.h。勾选Exclude file from build表示:屏蔽文件,不进行编译。

YHB123___ahnd的博客 271

SAP_SD模块路径及运输功能后台配置简介

后台路径:SPRO—后勤执行—装运—基本发运功能—定义路线—定义传输模式根据实际运输过程运输地理的类型:海运、公路运输、空运后台路径:SPRO—后勤执行—装运—基本发运功能—定义路线—定义发运类型根据实际运输过程运输方式:汽车、卡车、火车、轮船、飞机后台路径:SPRO—后勤执行—装运—基本发运功能—路径—定义路线—定义运输连接点指在运输过程中需要中转的地区可以维护中转地区的停留的时间后台路径:SPRO—后勤执行—装运—基本发运功能—路径—定义路线—定义路径和阶段。

我小时候很黑的博客 1395

MFC使用ToolKitPro界面库的学习笔记(1)

mfc使用 ToolKitPro 需要的环境配置和格式修改

代码星球的流浪猫 | 在Java废墟拾荒,MFC古迹散步,迪文屏上画符~ 下一站:未知星系!✨ 601

ABAP CDS SELECT, parameters 全面解析:把参数化视图玩明白(含可运行示例)

这篇技术博客介绍了ABAP CDS中参数化视图的用法与实践。文章从业务需求出发,解释了参数化视图的必要性,并详细说明了语法规则、参数类型匹配、视图内部参数引用等关键点。重点展示了在JOIN和ASSOCIATION中传递参数的写法,提供了航线分析的完整业务案例和可运行代码示例。文章还总结了视图实体与DDIC视图的差异、会话变量使用技巧等实践要点,特别强调了JOIN中必须显式传参的语法要求。通过真实场景演示,帮助开发者掌握参数化CDS的高效应用。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 362

PyTorch深度学习实战(4)——常用激活函数和损失函数详解

激活函数和损失函数是深度学习模型中重要组成部分,激活函数和损失函数的选择很大程度上决定了深度神经网络的性能和精度,需要根据具体问题的特点和数据分布等因素进行选择。在本节中,将介绍深度学习中常见的激活函数和损失函数,并说明不同函数的常见应用场景。

盼小辉丶的博客 1万+

MFC操作XML

MFC读取XML、删除修改XML、C++、MFC

断点 1911

“华为杯”研究生数学建模竞赛2019年-【华为杯】D题:汽车行驶工况构建(附获奖论文和MATLAB代码实现)

映了汽车道路行驶的运动学特征。准差、加速度标准差这九个特征参数用于后续的数据分析,建立了合理的汽车运动特征评。特征参数与所属类特征参数的特征值的相关性大小替代误差进行排序的,计算得到汽车行。对于问题一,我们首先对原始数据进行了时间变量转换,将原本的年月日时分秒的计。紧接着进行了运动学片段的划分,引入九个特征参数,通过主成分分析、本文研究的是汽车行驶工况构建,汽车行驶工况是描述汽车行驶的速度。的合理性评价标准,对两种方法都进行了合理性评价,得到的。进行了合理性评价,得到了最优的汽车行驶工况曲线。

getusushu的博客 1000

修改CMarkup, 控制节点空值的生成方式

前几天和同事讨论CMarkup的使用问题. 他生成的xml给别人用,

博客标题不能为空 1754

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

基于CMarkup的xml文件读写

基于CMarkup的xml文件读写写在前面:因为本文是基于CMarkup进行的xml文件的读写,所以需要参考CMarkup的官方网站 1.官方说明文档网站 http://www.firstobject.com/fast-start-to-xml-in-c++.htm 2.官方文件下载 http://www.firstobject.com/dn_markup.htm关于官方文档相关的方法解释请

守护天使的博客 5047

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

markup.cpp

// Markup.cpp: implementation of the CMarkup class. // // Markup Release 11.5 // Copyright (C) 2011 First Objective Software, Inc. All rights reserved // Go to www.firstobject.com for the latest CMark

u010236780的专栏 1642

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

C++ 读取XML文件 Markup.cpp的 CMarkup 读取XML

添加Markup.cpp和.h,使用时添加引用bool LoadModelCfg(char* FileName) { CMarkup xml; if (!xml.Load(FileName)) { return false; } xml.ResetPos(); if (xml.FindElem())//第一个根节点 { xml.IntoElem();//进入根目录 wh...

glmushroom的专栏 1100

vs2017 中使用markup解析xml

visual studio2017采用markup解析xml学习笔记 参考链接: markup语法以及代码 1、下载markup 下载链接 Release 11.5 zip (579k)C++ source code for Linux, Mac, Windows 本地解压得到: 2、vs2017中使用markup解析 2.1新建项目mark_test——windows控制台应用程序 2.1将解...

axiba_gun的博客 744

xml文件内容编辑及提取

首先,建立xml文件 a.xml  内容如下 name1 2 1.建立:1.1 c++方法  int myGenXml() { FILE *fp; char fname[100]; strcpy(fname, "a.xml"); fp = fopen(fname, "w+"); fprintf(fp, "\n%s", ""); fprintf(fp, "\n%s",

slm1130的博客 2926

Qt调用CMarkup解析XML出现编译问题

CMarkup是一个很优秀的开源的解析XML工具,C++编写的,支持跨平台。源码也只有两个文件,一个.h和一个.cpp文件。 把这两个文件添加到工程中然后编译一下,出现以下错误: 1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\atlmfc\include\afx.h(24): fatal error C1189: #erro...

wb175208的专栏 867
上一篇: VC解析XML--使用CMarkup类解析XML
下一篇: fopen()函数的参数说明
iteye_8601
博客等级 码龄8年 0粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值