Приложение STUpload: работа с постоянными данными

   
На этом шаге мы реализуем чтение данных из файла.

   
Начиная с этого шага ма встроим в приложение STUpload средства работы с постоянными данными, хранящимися
в файлах на локальном жестком диске. Модификация приложения будет проведена в два этапа. Во-первых,
мы изменим функцию CSTUploadDoc::LoadData(). CSTUploadDoc::OnDataImport() обращается функции
LoadData() для загрузки в приложение данных из текстового файла.

   
Во-вторых, реализуем стандартную MFC-сериализацию, чтобы импортированные из текстового файла данные
приложения можно было сохранить в файле документа STUpload.
Импортирование данных из текстового файла

   
В приложении STUpload мы создали временную реализацию функции CSTUploadDoc::LoadData().
В текущем состоянии она просто добавляет заданные в тексте программы записи о ценах акций в объект CStockDataList
(а точнее - в переменную CSTUploadDoc::m_DocList). Модифицируем функцию LoadData() таким образом,
чтобы она загружала эти записи в m_DocList из объекта CStdioFile, передаваемого из функции
CSTUploadDoc::OnDataImport(). Первая задача - воспользоваться объектом CStdioFile для открытия
выбранного пользователем текстового файла.

  • Открытие текстового файла.
  • Перейдите в функцию CSTUploadDoc::OnDataImport() и найдите в ее коде условный оператор:
      if (nID == IDOK)
    
  • После объявления объекта CStdioFile добавьте следующий код:
    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()
    для инициализации в приложении поля ввода с форматированием.

  • Создание поля ввода с форматированием.
  • Отредактируйте функцию CSTUploadApp::InitInstance(), добавив следующую строку непосредственно
    перед оператором return в конце тела функции:

        AfxInitRichEdit();
    
  • Создайте переменную-член DDX CConflictDialog::m_REditText. Эта переменная (типа CString) будет
    использоваться для задания строки, отображаемой в поле ввода с форматированием.
  • Создание переменной CConflictDialog::m_REditText.
  • Откройте ClassWizard.
  • Выберите вкладку Member Variables.
  • Создайте в классе CConflictDialog переменную m_REditText типа CString, связанную с идентификатором
    IDC_DUPL_RICHEDIT.


    Рис.2. Создание переменной CConflictDialog::m_REditText

  • Теперь Вы подготовились к тому, чтобы создать новую LoadData().

  • Замена функции LoadData().
  • В файле STUploadDoc.cpp добавьте к другим директивам #include следующую:
        #include "ConflictDialog.h"
    
  • Найдите и полностью удалите функцию CSTUploadDoc::LoadData(), после чего замените ее следующим кодом:
    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;
    }
    
  • Просмотрите код и убедитесь, что Вы понимаете, как:
    • используется временный список для хранения данных таким образом, чтобы они не повлияли на приложение
      до того, как загрузка успешно завершится и данные одобрит пользователь;
    • программа отличает строки заголовка и данных;
    • анализируется строка заголовка для проверки корректности типа загружаемого документа;
    • используются функции-члены CString для извлечения информации из строки данных;
    • программа справляется с неверными строками, дубликатами и взаимно исключающими записями (код
      ценной бумаги и дата одинаковы, но цены отличаются). Вероятно, Вам захочется познакомиться поближе с
      функцией CStockDataList::AddSorted().
  • Соберите и запустите приложение STUpload. Попытайтесь воспользоваться пунктом
    Import меню Data для загрузки файла Ch6Test.dat. Проверьте правильность загрузки.
    Закройте и снова запустите приложение. На этот раз загрузите файл conflict.dat с противоречивыми записями
    и посмотрите, как программа с ними справляется.

   
Текст измененного приложения вместе с необходимыми файлами можно взять здесь (65,7 Кб).

   
На следующем шаге мы рассмотрим сериализацию в STUpload.



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий