前回までの記事
CSV読込処理
さて、以前テキスト読込処理を作成しましたが、
あれは一時的な読込さえすればいい処理でした。
今回、できればテーブルの形としてデータを保存したいので
CMapクラスを使って保存に挑戦してみよと思います。
最初の方、CString型配列でなんとかできないかなーとは言ってましたが、
予定変更です。
ちょっと試したらこっちの方が簡単にできそうだったので。
そうは言っても、実は初めて作る処理なので果たしてうまくできるかは不明(;´・ω・)
まずはヘッダー。
マップを使うためにincludeを増やします。
#include <afx.h>
#include <vector>
#include <string>
class CTestResultDlg : public CDialogEx
{
//色々略
// 実装
private:
BOOL bCSVDataStore(const CString& filePath);// CSVデータの取込関数
std::vector<CString> GetTableData(const CString& strKey); // データ取得関数
private:
CMap<CString, LPCTSTR, std::vector<CString>, std::vector<CString>&> m_tableData;// 主キーとデータを保持するマップ
const int m_iColNum = 6; // CSV列数(ID、国語、数学、英語、理科、社会)
}
14行目のメンバマップが、CSVのデータ保持用です。
主キーはID列です。
15行目は列数。
そんな頻繁に使う変数ではないですが、こういうのもちゃんと変数化しておきます。
後10行目のbCSVDataStore、これ前回作りましたが、
引数(CSVファイルパス)が必要なので、追加しておきます。
最後に11行目、m_tableDataに入れたデータを引数を主キーとしてデータを取ってきます。
次いでcpp。
bCSVDataStoreの中身を変更します。
BOOL CTestResultDlg::bCSVDataStore(CString strFilePath)
{
BOOL bInit = TRUE; //最初の行判定
try
{
CStdioFile cFile;
if (cFile.Open(strFilePath, CFile::modeRead | CFile::typeText))
{
CString strLine = _T(""); // CSVの1行を格納
while (cFile.ReadString(strLine))
{
if (bInit)
{
// 最初の行はヘッダーなので無視
bInit = FALSE;
continue;
}
int iCurPos = 0;
CString strToken = strLine.Tokenize(_T(","), iCurPos);
std::vector<CString> tokens;
while (!strToken.IsEmpty())
{
tokens.push_back(strToken);
strToken = strLine.Tokenize(_T(","), iCurPos);
}
if (tokens.size() >= m_iColNum)
{
CString strKey = tokens[0]; // ID
tokens.erase(tokens.begin());
m_tableData[strKey] = tokens; //IDに対しての各データ
}
}
cFile.Close();
}
}
catch (const CFileException& e)
{
return FALSE;
}
catch (const std::exception& e)
{
// 予期しない標準例外
return FALSE;
}
return TRUE;
}
ファイルを開くため、念のため例外処理を入れておきます。
30行目のstrKeyがIDで、32行目のm_tableDataにデータを格納しています。
で、データをテーブルに格納するだけでは役に立たないので、
データを取り出す処理も作成。
std::vector<CString> CTestResultDlg::GetTableData(CString strKey)
{
std::vector<CString> values;
if (m_tableData.Lookup(strKey, values)) //キーに対するデータを取ってくるよ
{
return values;
}
return {};
}
とりあえず設定&取得処理はできました。
試しに動かす
さて試験的に動かしてみます。
取込ボタンが押されたときに呼ばれるOnBnClickedButtonCsvfileread内の最後に、
ちょっと付け足してみます。
一番広い、CSVデータ表示用の箇所に、1番最初のデータを表示させてみます。
void CTestResultDlg::OnBnClickedButtonCsvfileread()
{
//略
std::vector<CString> vecTemp = GetTableData(_T("1"));
CString strTemp = _T("");
strTemp.Format(_T("ID = %s, 国語 = %s, 数学 = %s, 英語 = %s, 理科 = %s, 社会 = %s, "),
_T("1"), vecTemp[0], vecTemp[1], vecTemp[2], vecTemp[3], vecTemp[4]);
SetDlgItemText(IDC_EDIT_DISP_DATA, strTemp);
}
表示させるデータはこれ。
ビルド後、該当CSVを取込すると…
できてる!
ちょっと感動( ;∀;)
追加処理
表示もできたので、最後に2つ追加しておきたいものがあるので。
まず一つ目はenumの定義。
もしかしたら使うかな、と思ったので。
(実際に使うかは現時点で不明)
ヘッダーに追加。
typedef enum
{
SUB_JA = 0, //国語 = 0
SUB_MA, //数学 = 1
SUB_EN, //英語 = 2
SUB_SC, //理科 = 3
SUB_SS, //社会 = 4
SUM_NUM //教科数 = 5
} SUBJECT;
もう一つの処理。
それは共有違反。
つまり、CSVを開いてるときにcFile.Openが使えないので例外処理に飛ばそうってやつです。
正直な話、今の処理でcatch (const CFileException& e)の方へ入ってくれると思ってましたが、
全然そうではなかったです。反省。
修正箇所だけ。
BOOL CTestResultDlg::bCSVDataStore(CString strFilePath)
{
BOOL bInit = TRUE; //最初の行判定
try
{
CStdioFile cFile;
CFileException e; //追加
if (cFile.Open(strFilePath, CFile::modeRead | CFile::typeText, &e))
{
//略
}
else
{
if (e.m_cause == CFileException::sharingViolation)
{
AfxMessageBox(_T("共有違反が発生しました。ファイルが他のプログラムで開かれている可能性があります。"));
return FALSE;
}
else
{
CString errorMsg;
e.GetErrorMessage(errorMsg.GetBuffer(256), 256);
errorMsg.ReleaseBuffer();
AfxMessageBox(_T("ファイルエラー: ") + errorMsg);
return FALSE;
}
}
}
catch (std::exception& e)
{
// 予期しない標準例外
CString errorMsg(e.what());
AfxMessageBox(_T("エラー: ") + errorMsg);
return FALSE;
}
catch (...)
{
AfxMessageBox(_T("不明なエラーが発生しました。"));
return FALSE;
}
return TRUE;
}
これで共有違反の場合、8行目のif文でelseの方に入ってくれます。
後は、それが共有違反かどうか。
ついでに色んな所の例外処理にメッセージを出しておきました。
なので、OnBnClickedButtonCsvfilereadに記載してたもともとのエラーメッセージは削除しておきます。
ビルドして、CSVを開いたまま取り込みを行うと
OKです。( ´∀`)bグッ!
終わりに
今回は主に、CSVデータの取り込みを行い、無事テーブルに入っているか確認できました。
次回は、レイアウト整えてデータを表示させたいなーってところです。
作ったはいいが例外処理の件。
すっかり置いてけぼりだけど直前にCSVフォーマットチェックしてるんだよな。
どう考えても共有違反チェックが先だと思うので、そこらへん処理の順変えないといけないですね。
そこも追々。
本日の成果物。
結局長ぇよ!問題解決してない。
ホント、どうするのが一番かなぁ?
折り畳みができるプラグイン探すか…?
TestResultDlg.h
// TestResultDlg.h : ヘッダー ファイル
//
#pragma once
#include <afx.h>
#include <vector>
#include <string>
typedef enum
{
SUB_JA = 0, //国語 = 0
SUB_MA, //数学 = 1
SUB_EN, //英語 = 2
SUB_SC, //理科 = 3
SUB_SS, //社会 = 4
SUM_NUM //教科数 = 5
} SUBJECT;
// CTestResultDlg ダイアログ
class CTestResultDlg : public CDialogEx
{
// コンストラクション
public:
CTestResultDlg(CWnd* pParent = nullptr); // 標準コンストラクター
// ダイアログ データ
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_TESTRESULT_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV サポート
// 実装
private:
BOOL bCSVFormatCheck();
BOOL bCSVDataStore(CString filePath);
void ButtonActive(BOOL bActive); //条件変更ボタン・表示ボタン活性判定
std::vector<CString> GetTableData(CString strKey); // データ取得関数
private:
CMap<CString, LPCTSTR, std::vector<CString>, std::vector<CString>&> m_tableData;
const int m_iColNum = 6; // CSV列数(ID、国語、数学、英語、理科、社会)
protected:
HICON m_hIcon;
// 生成された、メッセージ割り当て関数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedButtonCsvfileread();
};
TestResultDlg.cpp
// TestResultDlg.cpp : 実装ファイル
//
#include "pch.h"
#include "framework.h"
#include "TestResult.h"
#include "TestResultDlg.h"
#include "afxdialogex.h"
#include <stdexcept>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// アプリケーションのバージョン情報に使われる CAboutDlg ダイアログ
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// ダイアログ データ
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_ABOUTBOX };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV サポート
// 実装
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CTestResultDlg ダイアログ
CTestResultDlg::CTestResultDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_TESTRESULT_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CTestResultDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CTestResultDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_CSVFILEREAD, &CTestResultDlg::OnBnClickedButtonCsvfileread)
END_MESSAGE_MAP()
// CTestResultDlg メッセージ ハンドラー
BOOL CTestResultDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// "バージョン情報..." メニューをシステム メニューに追加します。
// IDM_ABOUTBOX は、システム コマンドの範囲内になければなりません。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != nullptr)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
// Framework は、この設定を自動的に行います。
SetIcon(m_hIcon, TRUE); // 大きいアイコンの設定
SetIcon(m_hIcon, FALSE); // 小さいアイコンの設定
// TODO: 初期化をここに追加します。
SetWindowText(_T("テスト結果表示ダイアログ")); //タイトル
ButtonActive(FALSE); //ボタン非活性化
return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}
void CTestResultDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// ダイアログに最小化ボタンを追加する場合、アイコンを描画するための
// 下のコードが必要です。ドキュメント/ビュー モデルを使う MFC アプリケーションの場合、
// これは、Framework によって自動的に設定されます。
void CTestResultDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 描画のデバイス コンテキスト
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// クライアントの四角形領域内の中央
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// アイコンの描画
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// ユーザーが最小化したウィンドウをドラッグしているときに表示するカーソルを取得するために、
// システムがこの関数を呼び出します。
HCURSOR CTestResultDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
// ↓↓↓↓↓↓ここから自作処理↓↓↓↓↓↓
/**
* @fn
* @brief 取込ボタン押下時の処理
* @param なし
* @return なし
*/
void CTestResultDlg::OnBnClickedButtonCsvfileread()
{
CString strTitle = _T("CSV読込処理"); //読込ダイアログタイトル
static char BASED_CODE szFilter[] = "CSV(カンマ区切り)|*.csv|";
CString strPath = _T(""); //ファイルパス
// CFileDialog インスタンスの作成
CFileDialog* dlgFile = new CFileDialog(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter);
dlgFile->m_ofn.lpstrTitle = strTitle; // タイトル設定
// ファイルダイアログの表示
INT_PTR iRetFileDlg = dlgFile->DoModal();
if (iRetFileDlg == IDOK)
{
// 開くボタンが押下された
//ファイルパス取得
strPath = dlgFile->GetPathName(); //パス取得
if (bCSVFormatCheck())
{
// チェックして問題なかった
if (bCSVDataStore(strPath))
{
// 格納が正常に行われた
SetDlgItemText(IDC_EDIT_CSVPATH, strPath); // パス設定
ButtonActive(TRUE);
}
else
{
// 問題があった
return;
}
}
else
{
// チェックして問題があった
MessageBox(_T("CSVフォーマットに誤りがあります。"));
return;
}
}
else
{
// キャンセルボタンが押下された
}
}
/**
* @fn
* @brief CSVフォーマットチェック
* @param なし
* @return TRUE : チェック問題なし
* FALSE : チェック問題あり
*/
BOOL CTestResultDlg::bCSVFormatCheck()
{
return TRUE;
}
/**
* @fn
* @brief データ取り込み
* @param CString strFilePath CSVファイルパス
* @return TRUE : 問題なし
* FALSE : 問題あり
*/
BOOL CTestResultDlg::bCSVDataStore(CString strFilePath)
{
BOOL bInit = TRUE; //最初の行判定
try
{
CStdioFile cFile;
CFileException e;
if (cFile.Open(strFilePath, CFile::modeRead | CFile::typeText, &e))
{
CString strLine = _T(""); // CSVの1行を格納
while (cFile.ReadString(strLine))
{
if (bInit)
{
// 最初の行はヘッダーなので無視
bInit = FALSE;
continue;
}
int iCurPos = 0;
CString strToken = strLine.Tokenize(_T(","), iCurPos);
std::vector<CString> tokens;
while (!strToken.IsEmpty())
{
tokens.push_back(strToken);
strToken = strLine.Tokenize(_T(","), iCurPos);
}
if (tokens.size() >= m_iColNum)
{
CString strKey = tokens[0]; // ID
tokens.erase(tokens.begin());
m_tableData[strKey] = tokens; //IDに対しての各データ
}
}
cFile.Close();
}
else
{
if (e.m_cause == CFileException::sharingViolation)
{
AfxMessageBox(_T("共有違反が発生しました。ファイルが他のプログラムで開かれている可能性があります。"));
return FALSE;
}
else
{
CString errorMsg;
e.GetErrorMessage(errorMsg.GetBuffer(256), 256);
errorMsg.ReleaseBuffer();
AfxMessageBox(_T("ファイルエラー: ") + errorMsg);
return FALSE; // 処理を終了
}
}
}
catch (std::exception& e)
{
// 予期しない標準例外
CString errorMsg(e.what());
AfxMessageBox(_T("エラー: ") + errorMsg);
return FALSE;
}
catch (...)
{
AfxMessageBox(_T("不明なエラーが発生しました。"));
return FALSE;
}
return TRUE;
}
/**
* @fn
* @brief ボタン活性非活性処理
* @param TRUE : 活性化
* FALSE : 非活性化
* @return なし
*/
void CTestResultDlg::ButtonActive(BOOL bActive)
{
//条件変更ボタン
CButton* pButton = (CButton*)GetDlgItem(IDC_BUTTON_CONDITIONAL);
pButton->EnableWindow(bActive);
//表示ボタン
pButton = (CButton*)GetDlgItem(IDC_BUTTON_DISP);
pButton->EnableWindow(bActive);
}
/**
* @fn
* @brief データ取得処理
* @param CString strKey 主キー
* @return vector<CString> 主キーに対する各データ
*/
std::vector<CString> CTestResultDlg::GetTableData(CString strKey)
{
std::vector<CString> values;
if (m_tableData.Lookup(strKey, values)) //キーに対するデータを取ってくるよ
{
return values;
}
return {};
}
コメント