На этом шаге мы реализуем чтение данных из файла.
Начиная с этого шага ма встроим в приложение 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.