//------------------------------------------------------------------------------ // FILE NAME: StringParser.cpp // HEADER: StringParser.h // // PURPOSE: Implementation of the CStringParser class. // // DESCRIPTION: This class acepts a string and immediately parses it into // separate fields based on the delimiter character specified by // the programmer. The separated fields are stored in a // CStringArray, and the 0th element in the array contains the // original string. Access to the separated fields is 1-based // (the first field is in index 1, and so on). // // If you are parsing quoted strings, it is IMPARATIVE that the // quoted strings be formatted correctly. Otherwise you will get // what appears to be bizarre results. // // DISCLAIMER: At first glance, it may seem like the code is somewhat verbose // and I might have taken the long way around to perform some // tasks, but I think it makes it easier for someone else to follow // when I do that. Maintainability is probably the most important // aspect of coding (after reliability of course). // // CHANGE HISTORY: // DATE INIT CHANGE DESCRIPTION // -------- ---- -------------------------------------------------------- // 07/20/2000 jms Initial code completed. // 08/09/2000 jms Added a way to submit a new string to facilitate the use // of the same object instance when a subsewquent string // needs to be parsed. // 01/12/2001 jms Added a way to find strings withion the parsed results. // 01/20/2001 jms Added support for quoted strings. //------------------------------------------------------------------------------ #include "stdafx.h" #include "StringParser.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif //------------------------------------------------------------------------------ // NAME: CStringParser::CStringParser() // // PURPOSE: Constructor - initializes the array of strings and calls the // ParseString() function. // // PARMETERS: CString sString The string to be parsed // char cDelimiter The delimiter character used to parse the // string // // RETURNS: N/A // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 07/20/2000 jms Initial development. //------------------------------------------------------------------------------ CStringParser::CStringParser(CString sString, char cDelimiter) { m_aStrings.SetSize(0,10); ResetOriginalString(sString, cDelimiter); } //------------------------------------------------------------------------------ // NAME: CStringParser::CStringParser() // // PURPOSE: Constructor - initializes the array of strings and calls the // ParseString() function. // // PARMETERS: CString sString The string to be parsed. // char cDelimiter The delimiter character used to parse the // string. // char cQuoter The quote character we should be looking // for. // // RETURNS: N/A // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- -------------------------------------------------------- // 01/20/2001 jms Added overloaded version of constructor to support // quoted strings. //------------------------------------------------------------------------------ CStringParser::CStringParser(CString sString, char cDelimiter, char cQuoter) { m_aStrings.SetSize(0,10); ResetOriginalString(sString, cDelimiter, cQuoter); } //------------------------------------------------------------------------------ // NAME: CStringParser::CStringParser() // // PURPOSE: Constructor - does nothing // // PARMETERS: CString sString The string to be parsed. // // RETURNS: N/A // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- -------------------------------------------------------- // 04/25/2001 jc Added overloaded version of constructor to support // initialization without other data //------------------------------------------------------------------------------ CStringParser::CStringParser() { m_aStrings.SetSize(0,10); } //------------------------------------------------------------------------------ // NAME: CStringParser::~CStringParser() // // PURPOSE: Destructor - removes all of the array elements from the // CStringArray. // // PARMETERS: None // // RETURNS: None // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 07/20/2000 jms Initial development. // 08/09/2000 jms Added call to new Clear() function. //------------------------------------------------------------------------------ CStringParser::~CStringParser() { Clear(); } //------------------------------------------------------------------------------ // NAME: CStringParser::Clear() // // PURPOSE: Clears the string table. // // PARMETERS: None // // RETURNS: None // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 08/09/2000 jms Added function. //------------------------------------------------------------------------------ void CStringParser::Clear() { if (m_nCount > 0) { m_aStrings.RemoveAll(); } } //------------------------------------------------------------------------------ // NAME: CStringParser::ResetOriginalString() // // PURPOSE: Clears the string table. // // PARMETERS: None // // RETURNS: None // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 08/09/2000 jms Added function. //------------------------------------------------------------------------------ void CStringParser::ResetOriginalString(CString sString, char cDelimiter) { Clear(); m_aStrings.Add(sString); m_cDelimiter = cDelimiter; m_cQuoter = '\0'; m_nCount = 0; ParseString(); } //------------------------------------------------------------------------------ // NAME: CStringParser::ResetOriginalString() // // PURPOSE: Clears the string table. // // PARMETERS: None // // RETURNS: None // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 08/09/2000 jms Added function. //------------------------------------------------------------------------------ void CStringParser::ResetOriginalString(CString sString, char cDelimiter, char cQuoter) { Clear(); m_aStrings.Add(sString); m_cDelimiter = cDelimiter; m_cQuoter = cQuoter; m_nCount = 0; ParseStringWithQuoter(); } //------------------------------------------------------------------------------ // NAME: CStringParser::GetField() // // PURPOSE: Returns the CString stored in the array element specified by // the nIndex parameter. If the specified index is greater than // the number of elements, it is an error. // // PARMETERS: int nIndex The index of the desired array item. // // RETURNS: CString sBuffer the string retrieved from the array. // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 07/20/2000 jms Initial development. //------------------------------------------------------------------------------ CString CStringParser::GetField(int nIndex) { CString sBuffer; sBuffer.Empty(); if (m_nCount >= nIndex) { sBuffer = m_aStrings.GetAt(nIndex); } else { sBuffer = "ERROR: Array index out of range."; } return sBuffer; } //------------------------------------------------------------------------------ // NAME: CStringParser::ParseString() // // PURPOSE: Separates the fields out of a copy of the original string by // searching for the specified delimiter character. As the string // is searched, it becomes shorter by deleting the most recently // found field (and the next delimiter, if one exists) from the // beginning of itself. // // PARMETERS: None // // RETURNS: int The number of fields found in the original string. // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 07/20/2000 jms Initial development. //------------------------------------------------------------------------------ int CStringParser::ParseString() { // get a copy of the string CString sWorkString = GetOriginalString(); CString sBuffer; int pos = 0; m_nCount = 0; // while the string is not empty... while (!sWorkString.IsEmpty()) { // find the position of the next delimiter pos = sWorkString.Find(m_cDelimiter); // if a delimiter is found if (pos >= 0) { // store the field sBuffer = sWorkString.Left(pos); // delete the field and the found delimiter sWorkString = sWorkString.Mid(pos + 1); } // otherwise, if a delimiter isn't found else { // the rest of the string is a field sBuffer = sWorkString; // and make the string empty sWorkString.Empty(); } // add the field to the CStringArray m_aStrings.Add(sBuffer); // increment the counter m_nCount++; } return m_nCount; } //------------------------------------------------------------------------------ // NAME: CStringParser::ParseStringWithQuoter() // // PURPOSE: Separates the fields out of a copy of the original string by // searching for the specified delimiter character. As the string // is searched, it becomes shorter by deleting the most recently // found field (and the next delimiter, if one exists) from the // beginning of itself. // // This version of ParseString handles a quoted string. A quoted // string may have the specified delimiter in an embedded between // quote characters, and this instance of the delimiter must be // ignored. // // PARMETERS: None // // RETURNS: int The number of fields found in the original string. // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 01/20/2001 jms Added this function. //------------------------------------------------------------------------------ int CStringParser::ParseStringWithQuoter() { // get a copy of the string CString sWorkString = GetOriginalString(); CString sBuffer; int pos = 0; m_nCount = 0; // while the string is not empty... while (!sWorkString.IsEmpty()) { // find the position of the next delimiter pos = sWorkString.Find(m_cDelimiter); // if a delimiter is found if (pos >= 0) { // if the first character is the specified quoter if (sWorkString.GetAt(0) == m_cQuoter) { // we have work to do sBuffer.Empty(); int nLength = 0; BOOL bDone = FALSE; do { //add the next character sBuffer += CString(sWorkString.GetAt(nLength)); nLength++; // if string is longer than one char and both ends are the quote char if (nLength > 1 && sBuffer.GetAt(nLength - 1) == m_cQuoter) { // we are done bDone = TRUE; } } while (!bDone); // adjust our work string sWorkString = sWorkString.Mid(nLength + 1); } else { sBuffer = sWorkString.Left(pos); sWorkString = sWorkString.Mid(pos + 1); } } // otherwise, if a delimiter isn't found else { // the rest of the string is a field sBuffer = sWorkString; // and make the string empty sWorkString.Empty(); } // add the field to the CStringArray m_aStrings.Add(sBuffer); // increment the counter m_nCount++; } return m_nCount; } //------------------------------------------------------------------------------ // NAME: CStringParser::FindExact() // // PURPOSE: Searches the entire array (excluding item 0) for an exact match // of the specified string. // // PARMETERS: CString sText The string we're looking for // int& nElement The element number of the found string // or SP_NOTFOUND if not found. // BOOL bCaseSensitive Case sensitivity matters // // RETURNS: CString The matching string if any. // // CHANGE LOG: // DATE INIT DESCRIPTION // ---------- ---- ----------------------------------------------------------- // 01/12/2001 jms Added function. //------------------------------------------------------------------------------ CString CStringParser::FindExact(CString sText, int* nElement, BOOL bCaseSensitive/*=FALSE*/) { if (!bCaseSensitive) { sText.MakeUpper(); } *nElement = SP_NOTFOUND; CString sResult = ""; BOOL bFound = FALSE; for (int i = 1; i <= m_nCount; i++) { sResult = m_aStrings.GetAt(i); if (!bCaseSensitive) { sResult.MakeUpper(); } if (sResult == sText) { *nElement = i; sResult = m_aStrings.GetAt(i); bFound = TRUE; break; } sResult.Empty(); } return sResult; } //------------------------------------------------------------------------------ // NAME: CStringParser::Find() // // PURPOSE: Searches the entire array (excluding item 0) for any string that // contains the specified string. // // PARMETERS: CString sText The string we're looking for // int& nElement The element number of the found string // or SP_NOTFOUND if not found. // BOOL bCaseSensitive Case sensitivity matters // // RETURNS: CString The matching string if any. // // CHANGE LOG: // DATE INIT DESCRIPTION // -------- ---- ------------------------------------------------------------- // 01/12/01 jms Added function. //------------------------------------------------------------------------------ CString CStringParser::Find(CString sText, int* nElement, BOOL bCaseSensitive/*=FALSE*/) { if (!bCaseSensitive) { sText.MakeUpper(); } *nElement = SP_NOTFOUND; CString sResult = ""; BOOL bFound = FALSE; for (int i = 1; i <= m_nCount; i++) { sResult = m_aStrings.GetAt(i); if (!bCaseSensitive) { sResult.MakeUpper(); } if (sResult.Find(sText) >= 0) { //save the position at which we found the string *nElement = i; // get it again (so we can get it in it's native form) sResult = m_aStrings.GetAt(i); bFound = TRUE; break; } sResult.Empty(); } return sResult; }