На этом шаге мы реализуем чтение данных из файла.
Начиная с этого шага ма встроим в приложение STUpload средства работы с постоянными данными, хранящимися в файлах на локальном жестком диске. Модификация приложения будет проведена в два этапа. Во-первых, мы изменим функцию CSTUploadDoc::LoadData(). CSTUploadDoc::OnDataImport() обращается функции LoadData() для загрузки в приложение данных из текстового файла.
Во-вторых, реализуем стандартную MFC-сериализацию, чтобы импортированные из текстового файла данные приложения можно было сохранить в файле документа STUpload.
В приложении STUpload мы создали временную реализацию функции CSTUploadDoc::LoadData(). В текущем состоянии она просто добавляет заданные в тексте программы записи о ценах акций в объект CStockDataList (а точнее - в переменную CSTUploadDoc::m_DocList). Модифицируем функцию LoadData() таким образом, чтобы она загружала эти записи в m_DocList из объекта CStdioFile, передаваемого из функции CSTUploadDoc::OnDataImport(). Первая задача - воспользоваться объектом CStdioFile для открытия выбранного пользователем текстового файла.
if (nID == IDOK)
CFileException fx; if ( !aFile.Open( aFileDialog.GetPathName(), CFile::modeRead | CFile::typeText, &fx ) ) { TCHAR buf[ 255 ]; fx.GetErrorMessage( buf, 255 ); CString strPrompt( buf ); AfxMessageBox( strPrompt ); return; }
void CSTUploadDoc::OnDataImport() { // Строка настройки диалогового окна File Dialog CString strFilter = "Data Files (*.dat)|*.dat|All Files (*.*)|*.*||"; CFileDialog aFileDialog( TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strFilter); int nID = aFileDialog.DoModal(); if(nID == IDOK) { CStdioFile aFile; CFileException fx; if ( !aFile.Open( aFileDialog.GetPathName(), CFile::modeRead | CFile::typeText, &fx ) ) { TCHAR buf[ 255 ]; fx.GetErrorMessage( buf, 255 ); CString strPrompt( buf ); AfxMessageBox( strPrompt ); return; } LoadData(aFile); }

Рис.1. Измененная функция OnDataImport()
Прежде чем заменить старую версию LoadData(), нужно модифицировать новую функцию, чтобы она могла работать с диалоговым окном Conflicting Records. Первым делом вызовите функцию AfxInitRichEdit() для инициализации в приложении поля ввода с форматированием.
AfxInitRichEdit();

Рис.2. Создание переменной CConflictDialog::m_REditText
#include "ConflictDialog.h"
BOOL CSTUploadDoc::LoadData(CStdioFile &infile)
{
// Проверка на неравенство NULL
ASSERT( infile.m_hFile != NULL );
// Данные размещаются во временном списке объектов CStocData
// и передаются в CSTUploadDoc::m_DocList только после
// успешного завершения загрузки.
CStockDataList TempList;
// Накапливающиеся записи записываем в существующие данные
TempList.AddHead( &m_DocList );
// Буфер строки
CString strTemp;
// Сегодняшняя дата
COleDateTime Today = COleDateTime::GetCurrentTime();;
COleDateTime FileDate;
CString strFileHeader;
int addedCtr = 0; // Счетчик успешно загруженных записей
int discardedCtr = 0; // Счетчик отброшенных записей
BOOL bFirstLine = TRUE;
while( infile.ReadString( strTemp ) )
{
BOOL bValidDate = FALSE;
CString strFund;
CString strDate;
// Отбрасываем пустые строки
if( strTemp.GetLength() == 0 ) continue;
if( bFirstLine )
{
// Извлекаем информацию из заголовка
strFileHeader = strTemp.Left(18);
strFileHeader.TrimRight();
strDate = strTemp.Mid( 18, 10 );
}
else
{
strFund = strTemp.Left(8);
strFund.TrimRight();
strDate = strTemp.Mid( 8, 10 );
}
int nYear = atoi( strDate.Right( 4 ));
int nMonth = atoi( strDate.Left( 2 ));
int nDay = atoi( strDate.Mid( 3, 2 ));
COleDateTime aDate( nYear, nMonth, nDay, 0, 0, 0 );
if( aDate.GetStatus() != COleDateTime::valid )
{
if( bFirstLine )
{
// Не удается считать дату файла - предполагаем,
// что она недействительна
AfxMessageBox( "Invalid File Format" );
return FALSE;
}
else
{
// Не удается считать дату записи - отбрасываем
// эту строку
discardedCtr++;
continue;
}
}
if ( bFirstLine )
{
// Извлекаем дату файла - возвращаемся в начало цикла
FileDate = aDate;
bFirstLine = FALSE;
continue;
}
double dPrice = atof( strTemp.Mid( 19 ));
// Создаем объект CStockData и добавляем его во временный массивt
CStockData aStData( strFund, aDate, dPrice );
CStockDataList::errorstatus err;
POSITION CurPos = TempList.AddSorted( aStData, err );
switch( err )
{
// Отбрасываем записи-дубликаты
case CStockDataList::duplicate_entry :
discardedCtr ++ ;
continue;
// Дублирующая запись с другим значением цены
case CStockDataList::conflicting_entry :
{
// Запрашиваем пользователя: хочет ли он отбросить
// дубликат, заменить его или не делать ничего
CConflictDialog aDialog;
// Создаем текст, который будет размещен
// в поле ввода с форматированием
CString strText = "Existing entry:\n\n";
CStockData SDTemp = TempList.GetAt( CurPos );
strText += SDTemp.GetAsString();
strText += "\n\nReplacement entry:\n\n";
strText += aStData.GetAsString();
// Полученный текст помещаем в поле ввода
aDialog.m_REditText = strText;
switch( aDialog.DoModal() )
{
case IDABORT : // Ничего не делаем
return FALSE;
case IDCANCEL : // Отбрасываем запись
discardedCtr++ ;
continue;
case IDOK : // Заменяем существующую запись
TempList.SetAt( CurPos, aStData );
}
}
default: // OK
addedCtr++ ;
}
}
// Добрались сюда - считанная запись действительна
CString strPrompt;
strPrompt.Format(
"Import of file %s complete:\nRecords loaded: %d \
\nRecords discarded: %d \
\n\nHit OK to load data into document.",
strFileHeader, addedCtr, discardedCtr );
if ( AfxMessageBox( strPrompt, MB_OKCANCEL ) == IDOK )
{
// Обновляем данные документа
m_DocList.RemoveAll();
m_DocList.AddHead( &TempList );
// Обновляем отображение ценной бумаги
CMainFrame * pWnd =
dynamic_cast< CMainFrame * > (AfxGetMainWnd());
if ( pWnd )
{
pWnd->UpdateFundList( m_DocList );
// После загрузки открываем окно ценных бумаг
pWnd->SetFundsVisible( TRUE );
}
return TRUE;
}
else
return FALSE;
}
Текст измененного приложения вместе с необходимыми файлами можно взять здесь (65,7 Кб).
На следующем шаге мы рассмотрим сериализацию в STUpload.