Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2005年05月14日] [更新 2015年10月13日] Delphi サンプルプログラム集
600_ADO でテキストファイルを読み出し専用接続
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
600_ADO_Text.zip [4,462 KB] 2015年10月13日版 (EXE 同梱)



  • 2010年05月25日
  • テキストファイルの種類ごとのプロジェクトに分けた
  • 動作確認環境変更
  • 間違った記述を修正
  • 2015年10月13日
  • 固定長の文字列の作成のサンプルを追加




ADO でテキストファイルを読出し専用接続


ADO でテキストファイルに接続する例です.テキストファイルでは,Paradox と同じで,対象ファイルがあるフォルダがデータベース,ファイルそのものがテーブル (表) という扱いになります.データ,レコードの変更を伴う SQL は実行できないようですが,選択クエリー等は実行できます.
例えば日本郵便では,郵便番号簿を CSV ファイルでも提供しています.読出し専用であってもデータベースとして扱えば条件選択が簡単になります.

テキストファイルが存在するフォルダ内に,schema.ini というファイルの種類,各項目の項目名 (フィールド名),項目の型とレコード長を設定したファイルが必要となります.
schema.ini ファイルがない場合は,CSV (カンマ区切り) テキストファイルとして扱われます.この場合,項目データ長の設定がないことになり,数値型列は 15 (Excel の最大有効桁),文字列型列は 255 (Excel の文字列の最大幅) となります.schema.ini に定義があればそれに従います.
これをスキーマファイルといいます.

スキーマ定義は,本ページのサンプルでは,1 つのファイルで保存しています.[ ] で囲まれた名前が,表示するファイル名です.
これを,データファイルであるテキストファイルと同じ [Data] フォルダに置いてあります.


本ページのサンプルは「読み出し専用」の接続ですが,テキストファイルの操作としての書き込みや変更は可能です.データの追加は,テキストファイルの末尾に追加します.
データを TDBGrid, TStringGrid 等の表示コントロールに表示した場合は,何行目という言い方ができますが,データベースそのものには行の概念はありません.データベースの表の何行目のデータを取り出すという操作はありません.



[ADOTestFIX.txt]
ColNameHeader=True
Format=FixedLength
Col1=食品群 Char Width 13
Col2=番号 Integer width 7
Col3=食品名 Char Width 19
Col4=キロカロリ Integer Width 13
[ADOTestTAB.txt]
ColNameHeader=True
MaxScanRows=0
Format=TabDelimited
Col1=食品群 Char Width 12
Col2=番号 Integer Width 5
Col3=食品名 Char Width 12
Col4=キロカロリ Integer width 8
[ADOTestCSV2.csv]
ColNameHeader=True
MaxScanRows=0
Format=CSVDelimited
Col1=食品群 Char Width 12
Col2=番号 Integer Width 8
Col3=食品名 Char Width 12
Col4=キロカロリ Integer width 8



以下は,サンプルプログラム集の ADO 関係の記事へのリンクです.





01_CSV ファイルに接続 ( スキーマ設定なし )


スキーマの設定なしで CSV テキストファイルに接続する例です.


図1
設計時画面
図2
実行結果

リスト1
スキーマの設定なしで CSV ファイルに接続
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, DBGrids, DB, ADODB, ExtCtrls;

type
  TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DataSource1: TDataSource;
    Panel1: TPanel;
    Panel2: TPanel;
    DBGrid1: TDBGrid;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  FormCreate時に各種初期設定
//  ADOConnecton1,ADOQuery1はFormに配置しデフォルトの状態で使用
//  それ以外の設定はこのサンプルコード内で行っている
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  ADOConnection1.Connected      := False;
  ADOConnection1.KeepConnection := False;
  ADOConnection1.LoginPrompt    := False;
  ADOConnection1.Mode           := cmRead;
  ADOConnection1.Provider       :='Microsoft.Jet.OLEDB.4.0';

  ADOQuery1.Connection := ADOConnection1;

  //テキストデータベースコントロールでは編集不可なので編集不可に
  DBGrid1.Options := DBGrid1.Options - [dgEditing];
end;
//=============================================================================
//  フォームを閉じたら接続を切断
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ADOConnection1.Connected := False;
end;

//=============================================================================
//  CSV(カンマ区切り)ファイルを読出し専用で表示
//
//  スキーマファイル内(schema.ini)で設定していないので,数値は15,文字列は255
//  の長さで表示される(エクセルのデフォルト値)
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LDBFileDir : String;
  LFileName  : String;
  LTableName : String;
begin
  Screen.Cursor := crHourGlass;

  //データベース名と表名を指定
  //テキストファイルはフォルダがデータベース名でファイル名が表名
  LDBFileDir := '../Data';
  LFileName  := 'ADOTestCSV1.csv';
  //表名の.は#に置換える
  LTableName:=StringReplace(LFileName, '.', '#', [rfReplaceAll]);

  //接続文字列関係
  ADOConnection1.ConnectionString := 'Data Source=' + LDBFileDir
                                   + ';Extended Properties="Text;"';

  //Windows VisataとWindows 7では接続した後に接続文字列を設定しないと正常動作不可
  ADOConnection1.Connected   := True;
  ADOQuery1.ConnectionString := ADOConnection1.ConnectionString;

  //データを読出す
  ADOQuery1.Close;
  try
    ADOQuery1.SQL.Clear;
    ADOQuery1.SQL.Add('SELECT * FROM ' + LTableName);
    ADOQuery1.Open;
  except
    raise;
  end;

  Screen.Cursor := crDefault;
end;

end.




02_CSV ファイルに接続 ( スキーマ設定あり )


次のスキーマファイルの設定を使用して,CSV テキストファイルに接続する例です.
スキーマファイルで,各項目 (フィールド) の長さを設定しているので,それに従っています.


[ADOTestCSV2.csv]
ColNameHeader=True
MaxScanRows=0
Format=CSVDelimited
Col1=食品群 Char Width 12
Col2=番号 Integer Width 8
Col3=食品名 Char Width 12
Col4=キロカロリ Integer width 8


図3
実行結果

リスト2
スキーマの設定を使用して CSV ファイルに接続
Button1Click 以外のコードは上のサンプルと同じ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, DBGrids, DB, ADODB, ExtCtrls;

type
  TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DataSource1: TDataSource;
    Panel1: TPanel;
    DBGrid1: TDBGrid;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  FormCreate時に各種初期設定
//  ADOConnecton1,ADOQuery1はFormに配置しデフォルトの状態で使用
//  それ以外の設定はこのサンプルコード内で行っている
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  ADOConnection1.Connected      := False;
  ADOConnection1.KeepConnection := False;
  ADOConnection1.LoginPrompt    := False;
  ADOConnection1.Mode           := cmRead;
  ADOConnection1.Provider       := 'Microsoft.Jet.OLEDB.4.0';

  ADOQuery1.Connection := ADOConnection1;

  //テキストデータベースコントロールでは編集不可なので編集不可に
  DBGrid1.Options := DBGrid1.Options - [dgEditing];
end;

//=============================================================================
//  フォームを閉じたら接続を切断
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ADOConnection1.Connected := False;
end;

//=============================================================================
//  CSV(カンマ区切り)ファイルを読出し専用で表示
//  schema.ini内の設定を使用.文字列の表示幅はWidthの設定値となる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LDBFileDir : String;
  LFileName  : String;
  LTableName : String;
begin
  Screen.Cursor := crHourGlass;

  //データベース名と表名を指定
  //テキストファイルはフォルダがデータベース名でファイル名が表名
  LDBFileDir := '../Data';
  LFileName  := 'ADOTestCSV2.csv';
  //表名の.は#に置換える
  LTableName := StringReplace(LFileName, '.', '#', [rfReplaceAll]);

  //接続文字列関係
  ADOConnection1.ConnectionString := 'Data Source=' + LDBFileDir
                                   + ';Extended Properties="Text;"';


  //Windows VisataとWindows 7では接続した後に接続文字列を設定しないと正常動作不可
  ADOConnection1.Connected   := True;
  ADOQuery1.ConnectionString := ADOConnection1.ConnectionString;

  //データを読出す
  ADOQuery1.Close;
  try
    ADOQuery1.SQL.Clear;
    ADOQuery1.SQL.Add('SELECT * FROM ' + LTableName);
    ADOQuery1.Open;
  except
    raise;
  end;

  Screen.Cursor := crDefault;
end;

end.




03_タブ区切りテキストファイルに接続


次のスキーマファイルの設定を使用して,タブ区切りテキストファイルに接続する例です.


[ADOTestTAB.txt]
ColNameHeader=True
MaxScanRows=0
Format=TabDelimited
Col1=食品群 Char Width 12
Col2=番号 Integer Width 5
Col3=食品名 Char Width 12
Col4=キロカロリ Integer width 8


図4
実行結果
  • 食品群が調味料だけを表示
  • キロカロリの値でソート

リスト3
タブ区切りテキストファイルに接続
Button1Click 以外のコードは上のサンプルと同じ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, DBGrids, DB, ADODB, ExtCtrls;

type
  TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DataSource1: TDataSource;
    Panel1: TPanel;
    DBGrid1: TDBGrid;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  FormCreate時に各種初期設定
//  ADOConnecton1,ADOQuery1はFormに配置しデフォルトの状態で使用
//  それ以外の設定はこのサンプルコード内で行っている
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  ADOConnection1.Connected      := False;
  ADOConnection1.KeepConnection := False;
  ADOConnection1.LoginPrompt    := False;
  ADOConnection1.Mode           := cmRead;
  ADOConnection1.Provider       := 'Microsoft.Jet.OLEDB.4.0';

  ADOQuery1.Connection := ADOConnection1;

  //テキストデータベースコントロールでは編集不可なので編集不可に
  DBGrid1.Options := DBGrid1.Options - [dgEditing];
end;

//=============================================================================
//  フォームを閉じたら接続を切断
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ADOConnection1.Connected := False;
end;

//=============================================================================
//  タブカンマ区切りテキストファイルを読出し専用で表示
//  schema.ini内の設定を使用.文字列の表示幅はWidthの設定値となる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LDBFileDir : String;
  LFileName  : String;
  LTableName : String;
begin
  Screen.Cursor := crHourGlass;

  //データベース名と表名を指定
  //テキストファイルはフォルダがデータベース名でファイル名が表名
  LDBFileDir := '../Data';
  LFileName  := 'ADOTestTAB.txt';
  //表名の.は#に置換える
  LTableName := StringReplace(LFileName, '.', '#', [rfReplaceAll]);

  //接続文字列関係
  ADOConnection1.ConnectionString := 'Data Source=' + LDBFileDir
                                   + ';Extended Properties="Text;"';


  //Windows VisataとWindows 7では接続した後に接続文字列を設定しないと正常動作不可
  ADOConnection1.Connected   := True;
  ADOQuery1.ConnectionString := ADOConnection1.ConnectionString;

  //データを読出す
  ADOQuery1.Close;
  try
    ADOQuery1.SQL.Clear;
    ADOQuery1.SQL.Add('SELECT * FROM ' + LTableName);
    ADOQuery1.SQL.Add('WHERE 食品群="調味料類"');
    ADOQuery1.SQL.Add('ORDER BY キロカロリ');
    ADOQuery1.Open;
  except
    raise;
  end;

  Screen.Cursor := crDefault;
end;

end.




04_固定長テキストファイルに接続


次のスキーマファイルの設定を使用して,固定長テキストファイルに接続する例です.


[ADOTestFIX.txt]
ColNameHeader=True
Format=FixedLength
Col1=食品群 Char Width 13
Col2=番号 Integer width 7
Col3=食品名 Char Width 19
Col4=キロカロリ Integer Width 13


図5
実行結果
  • 項目の表示順番を SQL で設定

リスト4
固定長テキストファイルに接続
Button1Click 以外のコード上のサンプルと同じ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, DBGrids, DB, ADODB, ExtCtrls;

type
  TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    ADOQuery1: TADOQuery;
    DataSource1: TDataSource;
    Panel1: TPanel;
    DBGrid1: TDBGrid;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  FormCreate時に各種初期設定
//  ADOConnecton1,ADOQuery1はFormに配置しデフォルトの状態で使用
//  それ以外の設定はこのサンプルコード内で行っている
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  ADOConnection1.Connected      := False;
  ADOConnection1.KeepConnection := False;
  ADOConnection1.LoginPrompt    := False;
  ADOConnection1.Mode           := cmRead;
  ADOConnection1.Provider       := 'Microsoft.Jet.OLEDB.4.0';

  ADOQuery1.Connection := ADOConnection1;

  //テキストデータベースコントロールでは編集不可なので編集不可に
  DBGrid1.Options := DBGrid1.Options - [dgEditing];
end;

//=============================================================================
//  フォームを閉じたら接続を切断
//=============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ADOConnection1.Connected := False;
end;

//=============================================================================
//  固定長テキストファイルを読出し専用で表示
//  schema.ini内の設定を使用.文字列の表示幅はWidthの設定値となる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LDBFileDir : String;
  LFileName  : String;
  LTableName : String;
begin
  Screen.Cursor := crHourGlass;

  //データベース名と表名を指定
  //テキストファイルはフォルダがデータベース名でファイル名が表名
  LDBFileDir := '../Data';
  LFileName  := 'ADOTestFIX.txt';
  //表名の.は#に置換える
  LTableName := StringReplace(LFileName, '.', '#', [rfReplaceAll]);

  //接続文字列関係
  ADOConnection1.ConnectionString := 'Data Source=' + LDBFileDir
                                   + ';Extended Properties="Text;"';


  //Windows VisataとWindows 7では接続した後に接続文字列を設定しないと正常動作不可
  ADOConnection1.Connected   := True;
  ADOQuery1.ConnectionString := ADOConnection1.ConnectionString;

  //データを読出す
  ADOQuery1.Close;
  try
    ADOQuery1.SQL.Clear;
    ADOQuery1.SQL.Add('SELECT 番号, 食品群, 食品名, キロカロリ FROM ' + LTableName);
    ADOQuery1.Open;
  except
    raise;
  end;

  Screen.Cursor := crDefault;
end;

end.




05_固定長フィールドの文字列の作成


上のサンプルで扱った固定長のフィールドを持つ文字列を作成するサンプルです.「桁揃え」で表示する等の用途にも利用できます.
結果が確認しやすいように,等倍フォントを使用しています.

Delphi 2007 以前の Ansi 版では,漢字等の全角文字は 2 バイト,半角の英数字は 1 バイトですが,Delphi 2009 以降では,文字のバイト数は固定ではありません.そのため,半角の文字数で換算した固定長さの文字列を作成するには工夫が必要です.

文字列を AnsiString 型にキャスト (変換) して,Length 関数で文字列の長さを取得すれば,全角文字は 2, 半角文字は 1 として取得できます.
しかし,AnsiString 型で扱えないユニコードな文字は 1 となってしまいます.そこで,本サンプルでは MECSUtils の MecsStringWidth 関数を使用しています.MecsStringWidth 関数は,半角文字数に換算した文字数を取得することができます.MECSUtils については [Delphi 2009 以降の Unicode に関する記事] の記事内にリンクを貼ってあります.



実行結果です.
Button1 は Format 関数を使用していますが,ユニコード文字列の場合,書式の s は文字数で指定します.したがって,半角文字換算にすると,全角文字の数だけ多い結果になります.
一方,Button2 の MecsStringWidth 関数を使用したコードでは期待通り設定できています.


図6
設計時画面
図7
Delphi XE(UP1) での実行結果
  • 上が Format 関数での結果
  • 下が MecsStringWidth 関数での結果
  • 左から
    1 番目の文字列は半角 10 文字の固定長
    2 番目の文字列は半角 15 文字の固定長
    3 番目の文字列は半角 10 文字の固定長

下図は Delphi 2007-R2 での実行結果です.
Ansi 版の Delphi では,Format 関数の書式 s は,バイト単位での指定です.したがって,期待通りの固定長の文字列が作成されています.
Delphi 2009 以降でも,半角の英数字だけを扱うのであれば,Format 関数で十分です.


図8
Delphi 2007-R2 での実行結果
  • 上が Format 関数での結果
  • 下が MecsStringWidth 関数での結果

リスト5
固定長フィールドの文字列の作成
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)

    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  MECSUtils;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Font.Name := 'MS ゴシック';
  Memo1.Font.Size := 10;

  Memo1.Lines.Clear;
  Memo1.Lines.Add('1234567890123456789012345678901234567890');
  Memo1.Lines.Add('----------------------------------------');
end;

//=============================================================================
//  固定長の文字列を作成
//  Format関数を使用する例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LRetText : String;
begin
  LRetText := Format('%-10s%-15s%-10s', ['ABC', '日本 太郎', '12345']);

  Memo1.Lines.Add(LRetText);
end;

//=============================================================================
//  固定長の文字列を作成
//  専用の関数を作成して使用
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);

  //---------------------------------------------------------------------------
  //  指定した長さの文字列を作成する関数
  //  文字列が指定長さより短い場合は半角の空白で埋める
  //  文字列の長さのチェックはしていないので注意
  //
  //  ASrcText : 対象の文字列
  //  ALen     : 設定する文字列の半角単位での長さ
  //  AJustify : 左寄せの時はtaLeftJustify(デフォルト).それ以外は右寄せ
  //
  //  usesにMECSUtilsが必要
  //---------------------------------------------------------------------------
  function CreateFixedLengthText(ASrcText: String; ALen: Integer;
    AJustify: TAlignment=taLeftJustify): String;
  var
    LSpcLen : Integer;
  begin
    if Length(ASrcText) = 0 then begin
      Result := StringOfChar(' ', ALen);
    end else begin
      LSpcLen := ALen - MecsStringWidth(ASrcText);
      if LSpcLen > 0 then begin
        if AJustify = taLeftJustify then begin
          Result  := ASrcText + StringOfChar(' ', LSpcLen);
        end else begin
          Result  := StringOfChar(' ', LSpcLen) + ASrcText;
        end;
      end else begin
        Result  := ASrcText;
      end;
    end;
  end;
  //---------------------------------------------------------------------------

var
  LRetText : String;
begin
  LRetText := CreateFixedLengthText('ABC', 10)
            + CreateFixedLengthText('日本 太郎', 15)
            + CreateFixedLengthText('12345', 10);

  Memo1.Lines.Add(LRetText);
end;

end.




06_固定長フィールドの文字列の作成 - AnsiString 型を使用する方法


上のサンプルの結果で分かるように,Ansi 版の Delphi であれば指定の固定長の文字列が作成でき,期待通りの桁揃えとなります.
したがって,ユニコードな文字を扱う必要がなければ,以下のように,文字列を AnsiString 型で処理すれば期待通りに表示桁を揃えることができます.Format 関数は AnsiStrings ユニット内の同名の関数を使用します.



図9
AnsiString 型を使用した結果
  • Delphi XE(UP1) での実行結果
  • 下は MecsStringWidth 関数での結果

リスト6
固定長フィールドの文字列の作成
AnsiString 型を使用する方法
//=============================================================================
//  固定長の文字列を作成
//  AnsiStringsのFormat関数を使用する例
//  usesにAnsiStringsが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LRetText : AnsiString;
  LText    : string;
begin
  LRetText := AnsiStrings.Format('%-10s%-15s%-10s', ['ABC', '日本 太郎', '12345']);

  //そのまま代入すると[文字列の暗黙的なキャスト]の警告となる
  LText := string(LRetText);
  Memo1.Lines.Add(LText);
end;