エントリのタイトルがすべてです。前の記事で、CSVファイルアクセスクラスを作ってみていたのですが、VCLのTStrings::CommaTextプロパティが、Commaだけでなく、Spaceも区切り文字とみなすことが分かりました。ややこしいプロパティ名しやがってこのー。
CSVファイルアクセスクラス - 涼の成長記録


CommaTextのリファレンスを読むと、しれっとこんなこと書いてあります。

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

TStrings.CommaText プロパティ


そもそも、SDF書式付きテキストって何ですか。SDFってなんですか。Self-Defense Forceで自衛隊ですか。Standard Delay Formatなんですか、標準遅延フォーマットなんですか、そうですか、聞いたことありませんよ、情報弱者の私には到底理解できません。
Standard Delay Format - Wikipedia, the free encyclopedia


嘆いていても始まらないので、別の方法を探していたのですが、TStringsには、Delimiterという心強いプロパティがおりました。私がうだうだ言うより、コードを見てもらった方が早いです。


こうやって取ると、半角スペースが区切り文字とみなされてしまうので、

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

file->LoadFromFile(m_FilePath);

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


Delimiterプロパティにコンマを指定し、StrictDelimiterをtrueにしておきます。値の取り出しは、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


いやー、プログラミングって楽しいですね。(こう言えば許されると思っている。)
ではでは。