65.9K
CodeProject is changing. Read more.
Home

Fast splitting of CString objects

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.67/5 (18 votes)

Mar 11, 2003

GPL3
viewsIcon

105905

Splitting CString objects is mostly easy, but can be tricky. I developed this function for using it with CSV files.

Introduction

Splitting CString objects is mostly easy. But in some special cases, like handling CSV data, the following function can do a good job: it explains how to handle strings that contain the separator itself in the text.

The solution is to 'escape' all separators that occur in the text itself and use a splitting function that is aware of this 'escape' (chMagic) character. The following function extends AfxGetSubString with this behaviour and is optimized to be as fast as possible while using CString.

The extracted CString substring is directly manipulated using the GetBufferSetLength member function.

Code

// Get substring, chMagic is a character which 
// makes chSep act as a normal character. 
// Return TRUE on success. 
inline BOOL GetSubString(CString& strSub, LPCTSTR lpszFullString, 
                         int iFullStringLen, int iSubString, TCHAR chSep, 
                         TCHAR chMagic) 
{ 
    int iPos, iPosOrig, iStartPos, iEndPos, iNumMagics; 
    TCHAR* pcSubString; 
    if((lpszFullString == NULL) || 
        (iFullStringLen == 0)) 
        return FALSE; 
    // Find substring begin 
    for(iStartPos = 0; (iStartPos < iFullStringLen) && (iSubString > 0); 
        iStartPos++) 
    { 
        // May be separator ? 
        if(*(lpszFullString + iStartPos) == chSep) 
        { 
            if(((iStartPos > 0) && 
                (*(lpszFullString + iStartPos - 1) != chMagic)) 
                || (iStartPos == 0)) 
            { 
                // Sure it is a separator! 
                iSubString--; 
            } 
        } 
    } 

    // Return empty string when nothing found 
    if(iSubString > 0) 
    { 
        strSub.Empty(); 
        return FALSE; 
    } 

    // Find substring end 
    iNumMagics = 0; 
    for(iEndPos = iStartPos; 
        iEndPos < iFullStringLen; 
        iEndPos++) 
    { 
        // Count magics 
        if(*(lpszFullString + iEndPos) == chMagic) 
        { 
            iNumMagics++; 
        } 
        // May be separator ? 
        if(*(lpszFullString + iEndPos) == chSep) 
        { 
            if(((iEndPos > 0) && (*(lpszFullString + iEndPos - 1) 
                != chMagic)) || (iEndPos == 0)) 
            { 
                // Sure it is the end 
                break; 
            } 
        } 
    } 

    // Copy substring 
    pcSubString = strSub.GetBufferSetLength(
        iEndPos - iStartPos - iNumMagics); 
    iPosOrig = iStartPos; 
    iEndPos -= iStartPos; 
    if(pcSubString != NULL) 
    { 
        for(iPos = 0; iPos < iEndPos; iPos += sizeof(TCHAR)) 
        { 
            if(*(lpszFullString + iPosOrig) != chMagic) 
            { 
                *(pcSubString + iPos) = *(lpszFullString + iPosOrig); 
            } 
            else 
            { 
                iPos -= sizeof(TCHAR); 
                iEndPos -= sizeof(TCHAR); 
            } 
            iPosOrig += sizeof(TCHAR); 
        } 
        *(pcSubString + iPos) = 0; 
        return TRUE; 
    } 
    return FALSE; 
};

Example

CString strSub, strLine("1,2,3\\,1,4,..."); 
int iLen = strLine.GetLength(); 
GetSubString(strSub, strLine, iLen, 0, ',', '\\'); // strSub: 1 
GetSubString(strSub, strLine, iLen, 1, ',', '\\'); // strSub: 2 
GetSubString(strSub, strLine, iLen, 2, ',', '\\'); // strSub: 3,1