Ryukalice

TStrings::CommaTextは 半角スペースも区切り文字とみなす

2013-11-25

エントリのタイトルがすべてです。VCL の TStrings::CommaText プロパティが、Comma だけでなく、Space も区切り文字とみなすことが分かりました。CommaText のリファレンスを読むと、しれっとこんなこと書いてあります。

CommaText を割り当てる場合,値は SDF 書式付きテキストとして解析されます。SDF 形式の場合,文字列はスペースまたはカンマで区切られ,任意で二重引用符で囲まれます。 引用元: TStrings.CommaText プロパティ

別の方法を探していたのですが、TStrings には、Delimiter という心強いプロパティがおりました。こうやって取ると、半角スペースが区切り文字とみなされてしまうので、

TStringList *file = new TStringList;    // ファイル全体分のデータ。
TStringList *line = new TStringList;    // 一行分のデータ。

file->LoadFromFile(m_FilePath);

line->CommaText = file->Strings[0];

Delimiter プロパティにコンマを指定し、StrictDelimitertrue にしておきます。値の取り出しは、CommaText でなく、DelimitedText で行います。

TStringList *file = new TStringList;    // ファイル全体分のデータ。
TStringList *line = new TStringList;    // 一行分のデータ。

file->LoadFromFile(m_FilePath);

// 区切り文字を明示的に指定。(カンマ)
line->Delimiter = ',';

// Delimiterプロパティに指定した区切り文字だけを区切り文字とみなす。
line->StrictDelimiter = true;

// CommaTextでなく、DelimitedTextで取り出す。
line->DelimitedText = file->Strings[0];

以上を踏まえると、以前の TCsvFileAccess.cpp は、以下のようになります。

/*!
    @file   TCsvFileAccess.cpp
    @brief  CSVファイルアクセスクラスの実装
*/

#include <vcl.h>
#pragma hdrstop

#include <IOUtils.hpp>
#include <vector>
#include "TCsvFileAccess.h"

#pragma package(smart_init)

namespace Common {
namespace FileAccess {

//------------------------------------------------------------------------------
// コンストラクター
//------------------------------------------------------------------------------
__fastcall TCsvFileAccess::TCsvFileAccess(const UnicodeString &filePath) : m_FilePath(filePath) {
    // フォルダーを作成しておく。
    try {
        ForceDirectories(TPath::GetDirectoryName(m_FilePath));
    }
    catch (...) {
        // 例外もぐもぐ。。
    }
}

//------------------------------------------------------------------------------
// ファイルがあればtrueを返す
//------------------------------------------------------------------------------
bool __fastcall TCsvFileAccess::isFileExist() {
    return FileExists(m_FilePath);
}

//------------------------------------------------------------------------------
// 読み込み。
//------------------------------------------------------------------------------
bool __fastcall TCsvFileAccess::read(CSV_VECTOR &array) {
    // 読込むファイルが無ければ異常
    if (!FileExists(m_FilePath)) {
         return false;
    }

    TStringList *line = new TStringList;    // 一行分のデータ。
    TStringList *file = new TStringList;    // ファイル全体分のデータ。

    try {   // 項目数エラーや、異常値等で簡単に例外が発生する。
        file->LoadFromFile(m_FilePath);
        line->Delimiter = ',';
        line->StrictDelimiter = true;
        for (unsigned int i = 0; i < array.size(); i++) {
            line->DelimitedText = file->Strings[i];
            for (unsigned int j = 0; j < array.at(i).size(); j++) {
                array.at(i).at(j) = line->Strings[j];
            }
        }
    }
    catch (...) {
        delete line; delete file;
        return false;
    }

    delete line; delete file;
    return true;
}

//------------------------------------------------------------------------------
// 書き込み。
//------------------------------------------------------------------------------
void __fastcall TCsvFileAccess::write(const CSV_VECTOR &array) {
    TStringList *line = new TStringList;    // 一行分のデータ。
    TStringList *file = new TStringList;    // ファイル全体分のデータ。

    file->Clear();
    for (unsigned int row = 0; row < array.size(); row++) {
        line->Clear();
        for (unsigned int col = 0; col < array.at(row).size(); col++) {
            line->Add(array.at(row).at(col));
        }
        file->Add(line->CommaText);
    }
    file->SaveToFile(m_FilePath);

    delete line; delete file;
}

}   // namespace FileAccess
}   // namespace Common