1112 lines
22 KiB
C++
1112 lines
22 KiB
C++
// ==========================================================================
|
|
// Class Implementation : COXCsvFile
|
|
// ==========================================================================
|
|
|
|
// Header file : OXCsvFile.h
|
|
|
|
// Version: 9.3
|
|
|
|
// This software along with its related components, documentation and files ("The Libraries")
|
|
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
|
|
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
|
|
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
|
|
// to obtain this file, or directly from our office. For a copy of the license governing
|
|
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
|
|
|
|
// //////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <stdafx.h>
|
|
#include <limits.h>
|
|
#include <float.h>
|
|
#include "OXCsvFile.h"
|
|
|
|
#include "UTBStrOp.h"
|
|
#include "UTB64Bit.h"
|
|
|
|
// _ttoi64 is incorrectly defined in TCHAR.H, so you need to undefine
|
|
// it and redefine it correctly to avoid a compile error when
|
|
// building a non-Unicode version of your application.
|
|
#ifdef _UNICODE
|
|
#ifdef _ttoi64
|
|
#undef _ttoi64
|
|
#define _ttoi64 _wtoi64
|
|
#endif
|
|
#else
|
|
#ifdef _ttoi64
|
|
#undef _ttoi64
|
|
#define _ttoi64 _atoi64
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// Internal constants
|
|
//
|
|
const TCHAR tcZero=_T('0');
|
|
const TCHAR tcNulc=_T('\0');
|
|
|
|
/**
|
|
static strip(CString& text)
|
|
|
|
This function takes the passed in CString object, and removes
|
|
any leading or trailing whitespace characters. The CString is
|
|
modified directly, returning a string that either starts and
|
|
ends with non-whitepace characters, or an empty string.
|
|
|
|
--- In : strText : Reference to the CString that is to be stripped.
|
|
--- Out : strText : The string, with all leading and trailing whitespace
|
|
removed.
|
|
--- Returns :
|
|
--- Effect :
|
|
**/
|
|
static void inline strip(CString& strText)
|
|
{
|
|
strText.TrimRight();
|
|
strText.TrimLeft();
|
|
}
|
|
|
|
IMPLEMENT_DYNAMIC(COXCsvFile, CStdioFile)
|
|
|
|
/**
|
|
Textual error messages for reporting the error to a user.
|
|
**/
|
|
LPCTSTR COXCsvFile::m_pstrErrorMsgs[]={
|
|
_TEXT("No Error"),
|
|
_TEXT("Invalid column index was specified"),
|
|
_TEXT("Invalid column name was specified"),
|
|
_TEXT("Column contained an invalid numeric value"),
|
|
_TEXT("Column data did not match data in set"),
|
|
_TEXT("Line was too long"),
|
|
_TEXT("Line contained too many records"),
|
|
};
|
|
|
|
COXCsvFile::COXCsvFile()
|
|
: CStdioFile(), m_nColumns(0), m_nLastError(errNone), m_bLineEmpty(FALSE),
|
|
m_tcFieldDelim(_T(',')), m_tcStringDelim(_T('\"'))
|
|
{
|
|
}
|
|
|
|
COXCsvFile::COXCsvFile(FILE *pFile)
|
|
: CStdioFile(pFile), m_nColumns(0), m_nLastError(errNone), m_bLineEmpty(FALSE),
|
|
m_tcFieldDelim(_T(',')), m_tcStringDelim(_T('\"'))
|
|
{
|
|
}
|
|
|
|
COXCsvFile::COXCsvFile(LPCTSTR lpszFileName, UINT nOpenFlags)
|
|
: CStdioFile(lpszFileName,nOpenFlags), m_nColumns(0), m_nLastError(errNone),
|
|
m_bLineEmpty(FALSE), m_tcFieldDelim(_T(',')), m_tcStringDelim(_T('\"'))
|
|
{
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
void COXCsvFile::AssertValid() const
|
|
{
|
|
CStdioFile::AssertValid();
|
|
ASSERT(m_nColumns==m_arrColumns.GetSize());
|
|
ASSERT(m_tcFieldDelim!=tcNulc);
|
|
ASSERT(m_tcStringDelim!=tcNulc);
|
|
}
|
|
|
|
void COXCsvFile::Dump(CDumpContext& dc) const
|
|
{
|
|
CStdioFile::Dump(dc);
|
|
dc << _T("Number of Columns: ") << m_nColumns << _T("\n");
|
|
dc << _T("Last Error: (") << GetLastError() << _T(") ") << GetLastErrorMsg() << _T("\n");
|
|
if(IsLineEmpty())
|
|
{
|
|
dc << _T("The current line is empty\n");
|
|
}
|
|
dc << _T("Field Delimiter: '") << m_tcFieldDelim << _T("'\n");
|
|
dc << _T("String Delimiter: '") << m_tcStringDelim << _T("'\n");
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////
|
|
// Column management functions //
|
|
/////////////////////////////////
|
|
void COXCsvFile::SetColumnInfo(int nIndex, LPCTSTR lpszName, Types nType, BOOL bQuote)
|
|
{
|
|
ASSERT(nIndex>=0 && nIndex<m_nColumns);
|
|
|
|
if(nIndex>=0 && nIndex<m_nColumns)
|
|
{
|
|
COXColumnData& data=m_arrColumns[nIndex];
|
|
data.m_sName=lpszName;
|
|
data.m_nType=nType;
|
|
data.m_bQuote=bQuote;
|
|
}
|
|
}
|
|
|
|
BOOL COXCsvFile::SetAliases(const CString& sName, const CStringArray& arrAliases)
|
|
{
|
|
if(FindColumn(sName)!=1)
|
|
{
|
|
//
|
|
// The original name was found, no need to look any further
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The original name was not found, look for the aliases
|
|
//
|
|
int nIndex;
|
|
int nAlias;
|
|
int nCount=PtrToInt(arrAliases.GetSize());
|
|
|
|
for(nAlias=0; nAlias<nCount; ++nAlias)
|
|
{
|
|
nIndex=FindColumn(arrAliases[nAlias]);
|
|
if(nIndex!=-1)
|
|
{
|
|
//
|
|
// We have found an alias, rename this column and return
|
|
//
|
|
m_arrColumns[nIndex].m_sName=sName;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No luck finding the original name, or any of its aliases
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL COXCsvFile::SetAliases(const CString& sName, LPCTSTR lpstrAliases[])
|
|
{
|
|
if(FindColumn(sName)!=-1)
|
|
{
|
|
//
|
|
// The original name was found, no need to look any further
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The original name was not found, look for the aliases
|
|
//
|
|
int nIndex;
|
|
int nAlias;
|
|
|
|
for(nAlias=0; lpstrAliases[nAlias]!=NULL; ++nAlias)
|
|
{
|
|
nIndex=FindColumn(lpstrAliases[nAlias]);
|
|
if(nIndex!=-1)
|
|
{
|
|
//
|
|
// We have found an alias, rename this column and return
|
|
//
|
|
m_arrColumns[nIndex].m_sName=sName;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No luck finding the original name, or any of its aliases
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
void COXCsvFile::SetColumns(LPCTSTR lpstrColumns[])
|
|
{
|
|
int nIndex, nCount;
|
|
|
|
SetError(errNone);
|
|
|
|
if(lpstrColumns==NULL)
|
|
{
|
|
//
|
|
// Base the number of columns off of the first line of the table
|
|
// read in. Do not use headers.
|
|
//
|
|
nCount=-1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// determine how many headers there are
|
|
//
|
|
for(nCount=0; lpstrColumns[nCount]!=NULL; ++nCount)
|
|
;
|
|
}
|
|
|
|
//
|
|
// store the header information
|
|
//
|
|
m_arrColumns.RemoveAll();
|
|
if(nCount>0)
|
|
m_arrColumns.SetSize(nCount);
|
|
m_nColumns=nCount;
|
|
|
|
for(nIndex=0; nIndex<nCount; ++nIndex)
|
|
{
|
|
m_arrColumns[nIndex].m_sName=lpstrColumns[nIndex];
|
|
}
|
|
}
|
|
|
|
void COXCsvFile::Initialize(int nColumns)
|
|
{
|
|
SetError(errNone);
|
|
|
|
m_arrColumns.RemoveAll();
|
|
if(nColumns>0)
|
|
m_arrColumns.SetSize(nColumns);
|
|
m_nColumns=nColumns;
|
|
}
|
|
|
|
void COXCsvFile::SetColumns(const CStringArray& arrColumns)
|
|
{
|
|
int nIndex;
|
|
|
|
SetError(errNone);
|
|
|
|
//
|
|
// store the header information
|
|
//
|
|
m_nColumns=PtrToInt(arrColumns.GetSize());
|
|
m_arrColumns.RemoveAll();
|
|
m_arrColumns.SetSize(m_nColumns);
|
|
|
|
for(nIndex=0; nIndex<m_nColumns; ++nIndex)
|
|
{
|
|
m_arrColumns[nIndex].m_strData=arrColumns[nIndex];
|
|
}
|
|
}
|
|
|
|
void COXCsvFile::WriteHeaders()
|
|
{
|
|
int nColumn;
|
|
|
|
SetError(errNone);
|
|
|
|
for(nColumn=0; nColumn<m_nColumns; ++nColumn)
|
|
{
|
|
WriteColumn(nColumn,m_arrColumns[nColumn].m_sName,TRUE);
|
|
}
|
|
WriteLine();
|
|
}
|
|
|
|
BOOL COXCsvFile::GetColumns(int nNumExpected)
|
|
{
|
|
m_arrColumns.RemoveAll();
|
|
m_arrColumns.SetSize(nNumExpected);
|
|
m_nColumns=nNumExpected;
|
|
|
|
if(ReadLine() || GetLastError()==errTooManyColumns)
|
|
{
|
|
if(m_nColumns<m_arrColumns.GetSize())
|
|
m_nColumns= PtrToInt(m_arrColumns.GetSize());
|
|
|
|
for(int nColumn=0; nColumn<m_nColumns; ++nColumn)
|
|
{
|
|
m_arrColumns[nColumn].m_sName=m_arrColumns[nColumn].m_strData;
|
|
m_arrColumns[nColumn].m_strData.Empty();
|
|
}
|
|
return m_nColumns==nNumExpected;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// output functions
|
|
//
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, LPCTSTR lpszText)
|
|
{
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
return WriteColumn(nColumn, lpszText, m_arrColumns[nColumn].m_bQuote);
|
|
}
|
|
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, LPCTSTR lpszText, BOOL bQuote)
|
|
{
|
|
CString strSpecialChars; // the field and string delimiters, and line break characters
|
|
|
|
strSpecialChars=m_tcFieldDelim;
|
|
strSpecialChars+=m_tcStringDelim;
|
|
strSpecialChars+=_T("\r\n");
|
|
|
|
SetError(errNone);
|
|
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Important: The string needs to be quoted if one of the following conditions
|
|
// is met:
|
|
// 1. The string contains one of the following characters:
|
|
// a. the field delimiter character
|
|
// b. the string delimiter character
|
|
// c. carraige return
|
|
// d. line feed
|
|
// 2. The string begins with a whitespace character
|
|
// 3. The string ends with a whitespace character
|
|
//
|
|
if(!bQuote
|
|
&& _tcscspn(lpszText,strSpecialChars)>=_tcslen(lpszText)
|
|
&& !_istspace(lpszText[0])
|
|
&& !_istspace(lpszText[_tcslen(lpszText)-1]))
|
|
{
|
|
m_arrColumns[nColumn].m_strData=lpszText;
|
|
}
|
|
else
|
|
{
|
|
CString strItem(m_tcStringDelim);
|
|
for(int nIndex=0; lpszText[nIndex]!=tcNulc; ++nIndex)
|
|
{
|
|
if(lpszText[nIndex]==m_tcStringDelim)
|
|
{
|
|
strItem+=m_tcStringDelim;
|
|
strItem+=m_tcStringDelim;
|
|
}
|
|
else
|
|
{
|
|
strItem+=lpszText[nIndex];
|
|
}
|
|
}
|
|
strItem+=m_tcStringDelim;
|
|
|
|
m_arrColumns[nColumn].m_strData=strItem;
|
|
m_arrColumns[nColumn].m_nType=tString;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
inline TCHAR toHex(int nDigit)
|
|
{
|
|
ASSERT(nDigit>=0 && nDigit<16);
|
|
|
|
return (TCHAR)((nDigit>=10) ? _T('a')+nDigit-10 : tcZero+nDigit);
|
|
}
|
|
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, unsigned char ucData, BOOL bHex)
|
|
{
|
|
TCHAR text[5];
|
|
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
|
|
if(bHex)
|
|
{
|
|
//
|
|
// generate a hexidecimal string
|
|
//
|
|
text[0]=tcZero;
|
|
text[1]=_T('x');
|
|
text[2]=toHex((ucData >> 4) & 0x0f);
|
|
text[3]=toHex(ucData & 0x0f);
|
|
text[4]=tcNulc;
|
|
}
|
|
else
|
|
{
|
|
if(ucData>=100)
|
|
{
|
|
//
|
|
// generate a 3 digit decimal string
|
|
//
|
|
text[0]=(TCHAR)(tcZero+(ucData/100));
|
|
text[1]=(TCHAR)(tcZero+((ucData%100)/10));
|
|
text[2]=(TCHAR)(tcZero+(ucData%10));
|
|
text[3]=tcNulc;
|
|
}
|
|
else if(ucData>=10)
|
|
{
|
|
//
|
|
// generate a 2 digit decimal string
|
|
//
|
|
text[0]=(TCHAR)(tcZero+(ucData/10));
|
|
text[1]=(TCHAR)(tcZero+(ucData%10));
|
|
text[2]=tcNulc;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// generate a 1 digit decimal string
|
|
//
|
|
text[0]=(TCHAR)(tcZero+ucData);
|
|
text[1]=tcNulc;
|
|
}
|
|
}
|
|
|
|
m_arrColumns[nColumn].m_strData=text;
|
|
m_arrColumns[nColumn].m_nType=tByte;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, unsigned short unData, BOOL bHex)
|
|
{
|
|
TCHAR text[7];
|
|
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
|
|
if(bHex)
|
|
{
|
|
//
|
|
// generate a hexidecimal value
|
|
//
|
|
text[0]=tcZero;
|
|
text[1]=_T('x');
|
|
text[2]=toHex((unData >> 12) & 0x0f);
|
|
text[3]=toHex((unData >> 8) & 0x0f);
|
|
text[4]=toHex((unData >> 4) & 0x0f);
|
|
text[5]=toHex(unData & 0x0f);
|
|
text[6]=tcNulc;
|
|
}
|
|
else
|
|
{
|
|
if(unData>=10000)
|
|
{
|
|
//
|
|
// generate a 5 digit decimal string
|
|
//
|
|
text[0]=(TCHAR)(tcZero+(unData/10000));
|
|
text[1]=(TCHAR)(tcZero+((unData%10000)/1000));
|
|
text[2]=(TCHAR)(tcZero+((unData%1000)/100));
|
|
text[3]=(TCHAR)(tcZero+((unData%100)/10));
|
|
text[4]=(TCHAR)(tcZero+(unData%10));
|
|
text[5]=tcNulc;
|
|
}
|
|
else if(unData>=1000)
|
|
{
|
|
//
|
|
// generate a 4 digit decimal string
|
|
//
|
|
text[0]=(TCHAR)(tcZero+(unData/1000));
|
|
text[1]=(TCHAR)(tcZero+((unData%1000)/100));
|
|
text[2]=(TCHAR)(tcZero+((unData%100)/10));
|
|
text[3]=(TCHAR)(tcZero+(unData%10));
|
|
text[4]=tcNulc;
|
|
}
|
|
else if(unData>=100)
|
|
{
|
|
//
|
|
// generate a 3 digit decimal string
|
|
//
|
|
text[0]=(TCHAR)(tcZero+(unData/100));
|
|
text[1]=(TCHAR)(tcZero+((unData%100)/10));
|
|
text[2]=(TCHAR)(tcZero+(unData%10));
|
|
text[3]=tcNulc;
|
|
}
|
|
else if(unData>=10)
|
|
{
|
|
//
|
|
// generate a 2 digit decimal string
|
|
//
|
|
text[0]=(TCHAR)(tcZero+(unData/10));
|
|
text[1]=(TCHAR)(tcZero+(unData%10));
|
|
text[2]=tcNulc;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// generate a 1 digit decimal string
|
|
//
|
|
text[0]=(TCHAR)(tcZero+unData);
|
|
text[1]=tcNulc;
|
|
}
|
|
}
|
|
|
|
m_arrColumns[nColumn].m_strData=text;
|
|
m_arrColumns[nColumn].m_nType=tShort;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, unsigned long ulData, BOOL bHex)
|
|
{
|
|
TCHAR text[20];
|
|
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
|
|
if(bHex)
|
|
{
|
|
UTBStr::stprintf(text, 20, _T("0x%08x"), ulData);
|
|
}
|
|
else
|
|
{
|
|
UTBStr::stprintf(text, 20, _T("%d"), ulData);
|
|
}
|
|
|
|
m_arrColumns[nColumn].m_strData=text;
|
|
m_arrColumns[nColumn].m_nType=tLong;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, short nData, BOOL bHex)
|
|
{
|
|
TCHAR text[20];
|
|
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
|
|
if(bHex)
|
|
{
|
|
UTBStr::stprintf(text, 20, _T("0x%04x"), nData);
|
|
}
|
|
else
|
|
{
|
|
UTBStr::stprintf(text, 20, _T("%d"), nData);
|
|
}
|
|
|
|
m_arrColumns[nColumn].m_strData=text;
|
|
m_arrColumns[nColumn].m_nType=tShort;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, long lData, BOOL bHex)
|
|
{
|
|
TCHAR text[20];
|
|
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
|
|
if(bHex)
|
|
{
|
|
UTBStr::stprintf(text, 20, _T("0x%08x"), lData);
|
|
}
|
|
else
|
|
{
|
|
UTBStr::stprintf(text, 20, _T("%d"), lData);
|
|
}
|
|
|
|
m_arrColumns[nColumn].m_strData=text;
|
|
m_arrColumns[nColumn].m_nType=tLong;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, float fData)
|
|
{
|
|
TCHAR text[20];
|
|
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
|
|
UTBStr::stprintf(text, 20, _T("%f"), fData);
|
|
|
|
//
|
|
// Use the string write column in case quoting is needed. Some nations use comma
|
|
// as the decimal character, and also to account for the variable delimiter characters
|
|
// that might be used.
|
|
//
|
|
WriteColumn(nColumn, text);
|
|
m_arrColumns[nColumn].m_nType=tFloat;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL COXCsvFile::WriteColumn(int nColumn, double dData)
|
|
{
|
|
TCHAR text[20];
|
|
|
|
if(nColumn<0 || nColumn>=m_nColumns)
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
return FALSE;
|
|
}
|
|
|
|
UTBStr::stprintf(text, 20, _T("%f"), dData);
|
|
|
|
//
|
|
// Use the string write column in case quoting is needed. Some nations use comma
|
|
// as the decimal character, and also to account for the variable delimiter characters
|
|
// that might be used.
|
|
//
|
|
WriteColumn(nColumn, text);
|
|
m_arrColumns[nColumn].m_nType=tDouble;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void COXCsvFile::WriteLine()
|
|
{
|
|
CString strLine;
|
|
//
|
|
// Run through the columns, adding each of them to the line for output to the file.
|
|
// Clear the data value for each column after it has been added.
|
|
//
|
|
for(int nColumn=0; nColumn<m_nColumns; ++nColumn)
|
|
{
|
|
COXColumnData &data=m_arrColumns[nColumn];
|
|
if(!data.m_strData.IsEmpty())
|
|
{
|
|
if(data.m_nType==tFloat || data.m_nType==tDouble)
|
|
{
|
|
//
|
|
// Special check-look for field delimiter. This is because some
|
|
// nations use commas where the US uses decimal points.
|
|
//
|
|
if(data.m_strData.Find(m_tcFieldDelim)!=-1
|
|
|| data.m_strData.Find(m_tcStringDelim)!=-1)
|
|
{
|
|
//
|
|
// Need to quote this value, write it out as if it were a string
|
|
//
|
|
CString tmp=data.m_strData;
|
|
Types nType=data.m_nType;
|
|
|
|
WriteColumn(nColumn, tmp, TRUE);
|
|
data.m_nType=nType;
|
|
data.m_strData=tmp;
|
|
}
|
|
}
|
|
strLine+=data.m_strData;
|
|
}
|
|
if(nColumn!=m_nColumns-1)
|
|
strLine+=m_tcFieldDelim;
|
|
|
|
//
|
|
// clear out the data for the next line
|
|
//
|
|
m_arrColumns[nColumn].m_strData.Empty();
|
|
}
|
|
strLine+="\n";
|
|
WriteString(strLine);
|
|
}
|
|
|
|
/////////////////////
|
|
// input functions //
|
|
/////////////////////
|
|
BOOL COXCsvFile::ReadLine()
|
|
{
|
|
CString strLine;
|
|
BOOL bRetval=FALSE;
|
|
|
|
SetError(errNone);
|
|
m_bLineEmpty=TRUE;
|
|
|
|
//
|
|
// clear out previous line's data
|
|
//
|
|
int nIndex=0;
|
|
for(nIndex=0; nIndex<m_arrColumns.GetSize(); ++nIndex)
|
|
m_arrColumns[nIndex].m_strData.Empty();
|
|
|
|
if(ReadString(strLine))
|
|
{
|
|
//
|
|
// Append a newline character to mark the end of the line, as this is removed
|
|
// by ReadString.
|
|
//
|
|
strLine+=TEXT("\n");
|
|
|
|
//
|
|
// get the length of the line that has been read in
|
|
//
|
|
int nLength=strLine.GetLength();
|
|
|
|
//
|
|
// begin processing the line
|
|
//
|
|
int nColumn=0;
|
|
BOOL bInQuote=FALSE;
|
|
CString strText;
|
|
CString strBlanks;
|
|
TCHAR tcNext;
|
|
|
|
//
|
|
// Loop through the characters in the line, parsing out the individual fields
|
|
//
|
|
for(nIndex=0; nIndex<nLength; ++nIndex)
|
|
{
|
|
tcNext=strLine[nIndex];
|
|
if(tcNext==m_tcStringDelim)
|
|
{
|
|
//
|
|
// A quoted string
|
|
//
|
|
if(!strBlanks.IsEmpty())
|
|
{
|
|
strText+=strBlanks;
|
|
strBlanks.Empty();
|
|
}
|
|
bInQuote=TRUE;
|
|
|
|
//
|
|
// loop through looking for the end of the quoted string
|
|
//
|
|
for(++nIndex; bInQuote && nIndex<nLength; ++nIndex)
|
|
{
|
|
tcNext=strLine[nIndex];
|
|
if(tcNext==m_tcStringDelim)
|
|
{
|
|
//
|
|
// End of string or inset the delimiter into the field?
|
|
//
|
|
if(strLine[nIndex+1]==m_tcStringDelim)
|
|
{
|
|
//
|
|
// place a quote into the string
|
|
//
|
|
strText+=tcNext;
|
|
++nIndex;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// reached the end of the string
|
|
//
|
|
--nIndex;
|
|
bInQuote=FALSE;
|
|
}
|
|
}
|
|
else if(tcNext==_T('\n'))
|
|
{
|
|
strText+=tcNext;
|
|
|
|
//
|
|
// The end of the line has been reached, while within
|
|
// a quote, need to get the next line
|
|
//
|
|
if(ReadString(strLine))
|
|
{
|
|
nIndex=-1;
|
|
strLine+=_TEXT("\n");
|
|
nLength=strLine.GetLength();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This line was incomplete
|
|
//
|
|
SetError(errIncompleteLine);
|
|
nIndex=strLine.GetLength();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strText+=tcNext;
|
|
}
|
|
}
|
|
//inQuote=FALSE;
|
|
}
|
|
else if(tcNext==_T('\n'))
|
|
{
|
|
//
|
|
// we have reached the end of the line, and therefore an item
|
|
//
|
|
nIndex=strLine.GetLength();
|
|
|
|
if(nColumn==m_nColumns)
|
|
SetError(errTooManyColumns);
|
|
if(nColumn>=m_arrColumns.GetSize())
|
|
{
|
|
m_arrColumns.SetSize(nColumn+1);
|
|
m_nColumns=nColumn+1;
|
|
}
|
|
if (nColumn)
|
|
m_bLineEmpty=FALSE;
|
|
else
|
|
m_bLineEmpty=TRUE;
|
|
m_arrColumns[nColumn].m_strData=strText;
|
|
++nColumn;
|
|
strBlanks.Empty();
|
|
strText.Empty();
|
|
}
|
|
else if(tcNext==m_tcFieldDelim)
|
|
{
|
|
//
|
|
// We have reached the end of an item
|
|
//
|
|
if(nColumn==m_nColumns)
|
|
SetError(errTooManyColumns);
|
|
if(nColumn>=m_arrColumns.GetSize())
|
|
{
|
|
m_arrColumns.SetSize(nColumn+1);
|
|
m_nColumns=nColumn+1;
|
|
}
|
|
|
|
m_bLineEmpty=FALSE;
|
|
m_arrColumns[nColumn].m_strData=strText;
|
|
++nColumn;
|
|
|
|
strBlanks.Empty();
|
|
strText.Empty();
|
|
}
|
|
else if(_istspace(tcNext))
|
|
{
|
|
if(!strText.IsEmpty())
|
|
{
|
|
strBlanks+=tcNext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!strBlanks.IsEmpty())
|
|
{
|
|
strText+=strBlanks;
|
|
strBlanks.Empty();
|
|
}
|
|
strText+=tcNext;
|
|
}
|
|
}
|
|
if(!strText.IsEmpty())
|
|
{
|
|
if(nColumn==m_nColumns)
|
|
SetError(errTooManyColumns);
|
|
if(nColumn>=m_arrColumns.GetSize())
|
|
{
|
|
m_arrColumns.SetSize(nColumn+1);
|
|
m_nColumns=nColumn+1;
|
|
}
|
|
m_arrColumns[nColumn].m_strData=strText;
|
|
m_bLineEmpty=FALSE;
|
|
}
|
|
|
|
if(m_nColumns==-1)
|
|
{
|
|
m_nColumns=nColumn;
|
|
if(GetLastError()==errTooManyColumns)
|
|
SetError(errNone);
|
|
}
|
|
|
|
bRetval=(GetLastError()==errNone);
|
|
}
|
|
|
|
return bRetval;
|
|
}
|
|
|
|
int COXCsvFile::FindColumn(LPCTSTR lpszName) const
|
|
{
|
|
int nIndex;
|
|
|
|
for(nIndex=0; nIndex<m_nColumns; ++nIndex)
|
|
{
|
|
if(m_arrColumns[nIndex].m_sName.CompareNoCase(lpszName)==0)
|
|
return nIndex;
|
|
}
|
|
|
|
SetError(errColName);
|
|
return -1;
|
|
}
|
|
|
|
BOOL COXCsvFile::ReadColumn(int nColumn, CString& strText)
|
|
{
|
|
strText.Empty();
|
|
if(nColumn>=0 && nColumn<m_arrColumns.GetSize())
|
|
{
|
|
strText=m_arrColumns[nColumn].m_strData;
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL COXCsvFile::ReadColumn(int nColumn, unsigned char& ucData)
|
|
{
|
|
ucData=0;
|
|
if(nColumn>=0 && nColumn<m_arrColumns.GetSize())
|
|
{
|
|
int nValue=0;
|
|
CString text=m_arrColumns[nColumn].m_strData;
|
|
|
|
nValue=_ttoi(text);
|
|
|
|
if(nValue>=0 && nValue<=UCHAR_MAX)
|
|
{
|
|
ucData=(unsigned char)nValue;
|
|
return TRUE;
|
|
}
|
|
SetError(errNumericValue);
|
|
}
|
|
else
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL COXCsvFile::ReadColumn(int nColumn, unsigned short& unData)
|
|
{
|
|
unData=0;
|
|
if(nColumn>=0 && nColumn<m_arrColumns.GetSize())
|
|
{
|
|
long lValue=0;
|
|
CString text=m_arrColumns[nColumn].m_strData;
|
|
|
|
lValue=_ttol(text);
|
|
|
|
if(lValue>=0 && lValue<=USHRT_MAX)
|
|
{
|
|
unData=(unsigned short)lValue;
|
|
return TRUE;
|
|
}
|
|
SetError(errNumericValue);
|
|
}
|
|
else
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL COXCsvFile::ReadColumn(int nColumn, int& nDataIndex, LPCTSTR lpstrSet[])
|
|
{
|
|
CString strText, strItem;
|
|
int nIndex;
|
|
|
|
nDataIndex=0;
|
|
if(nColumn>=0 && nColumn<m_arrColumns.GetSize())
|
|
{
|
|
strText=m_arrColumns[nColumn].m_strData;
|
|
strText.MakeLower();
|
|
|
|
for(nIndex=0; lpstrSet[nIndex]!=NULL; ++nIndex)
|
|
{
|
|
strItem=lpstrSet[nIndex];
|
|
strItem.MakeLower();
|
|
if(strText==strItem)
|
|
{
|
|
nDataIndex=nIndex;
|
|
return TRUE;
|
|
}
|
|
}
|
|
SetError(errNotInSet);
|
|
}
|
|
else
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL COXCsvFile::ReadColumn(int nColumn, short& nData)
|
|
{
|
|
nData=0;
|
|
if(nColumn>=0 && nColumn<m_arrColumns.GetSize())
|
|
{
|
|
long nValue=0;
|
|
CString text=m_arrColumns[nColumn].m_strData;
|
|
|
|
nValue=_ttol(text);
|
|
|
|
if(nValue>=SHRT_MIN && nValue<=SHRT_MAX)
|
|
{
|
|
nData=(short)nValue;
|
|
return TRUE;
|
|
}
|
|
SetError(errNumericValue);
|
|
}
|
|
else
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL COXCsvFile::ReadColumn(int nColumn, long& lData)
|
|
{
|
|
lData=0;
|
|
if(nColumn>=0 && nColumn<m_arrColumns.GetSize())
|
|
{
|
|
__int64 lValue=0;
|
|
CString text=m_arrColumns[nColumn].m_strData;
|
|
|
|
lValue=_ttoi64(text);
|
|
|
|
if(lValue>=LONG_MIN && lValue<=LONG_MAX)
|
|
{
|
|
lData=(long)lValue;
|
|
return TRUE;
|
|
}
|
|
SetError(errNumericValue);
|
|
}
|
|
else
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL COXCsvFile::ReadColumn(int nColumn, float& fData)
|
|
{
|
|
fData=0.0;
|
|
if(nColumn>=0 && nColumn<m_arrColumns.GetSize())
|
|
{
|
|
double fValue=0;
|
|
LPTSTR ptr;
|
|
|
|
CString text=m_arrColumns[nColumn].m_strData;
|
|
|
|
fValue=_tcstod(text, &ptr);
|
|
|
|
if(fValue>=(1-FLT_MAX) && fValue<=FLT_MAX)
|
|
{
|
|
fData=(float)fValue;
|
|
return TRUE;
|
|
}
|
|
SetError(errNumericValue);
|
|
}
|
|
else
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL COXCsvFile::ReadColumn(int nColumn, double& dData)
|
|
{
|
|
dData=0.0;
|
|
if(nColumn>=0 && nColumn<m_arrColumns.GetSize())
|
|
{
|
|
double fValue=0;
|
|
LPTSTR ptr;
|
|
CString text=m_arrColumns[nColumn].m_strData;
|
|
|
|
fValue=_tcstod(text, &ptr);
|
|
|
|
if(fValue>=(1-DBL_MAX) && fValue<=DBL_MAX)
|
|
{
|
|
dData=(double)fValue;
|
|
return TRUE;
|
|
}
|
|
SetError(errNumericValue);
|
|
}
|
|
else
|
|
{
|
|
SetError(errBadColumnIndex);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|