2012年9月11日火曜日

Joinのオーバーロード

Joinのオーバーロードを試してみた。
但し、
class function Join(const Separator: string; const Values: IEnumerable): string; overload; static;
以外です。
詳細はプログラム中のコメントに記述しました。
以下、プログラム



program Project3;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Variants;


var
  s : string;
  a,b : single;
  s1,S2: string;
  elm : TArray;
  //sh : TStringHelper;
begin
  try
    { TODO -oUser -cConsole メイン : ここにコードを記述してください }
   //sh : TStringHelper

   //オーバーロードの1つ目
   //オープン配列を使用した形です。
   writeln('overload1:オープン配列');
   s := s.Join(',',['ミリー','ハイネ']);
   write('OK:');
   writeln(s);
   writeln;

   s1 := '2.17'; s2 := '9.19';
   s := s.Join(',',[s1,s2]);
   write('OK:');
   writeln(s);
   writeln;

   //文字列以外の型だとうまく出力できないようです。
   //空文字が出力されます。
   a := 2.17; b := 9.19;
   s := s.Join(',',[a,b]);
   write('NG');
   writeln(s);
   writeln;


   //オーバーロードの2つ目
   //文字列配列の結合開始位置(0基数)と数を指定
   //この例では、ニーナ,ベルト・サタンと表示します。
   //文字列配列作成の為にとりあえず分割
   s := 'ドッペ,パックン,ニーナ,ベルト・サタン,キノッピー';
   elm := s.Split([',']);
   writeln('overload2:開始位置と数を指定');
   writeln('元の文字列配列');
   for s1 in elm do
   begin
     writeln(s1);
   end;
   writeln('0基数で2番目の文字列から2個の文字列を結合');
   s := s.Join(',',elm,2,2);
   writeln(s);
   writeln;

   readln;



  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


実行結果は

です。

2012年9月10日月曜日

Splitのオーバーロード

Delphi XE3のオーバーロードを試してみた。
各オーバーロードの内容はプログラム中のコメントに
記載しました。

program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

var
  s,s1 : string;
  elm : TArray;
  elm2 : TArray;
  //sh : TStringHelper;
begin
  try
    { TODO -oUser -cConsole メイン : ここにコードを記述してください }
   //sh : TStringHelper
   s := 'ドッペ,パックン,ニーナ,,ベルト・サタン,,キノッピー';
   //Writeln(s);

   //カンマで分割
   //オーバーロードの1つ目、第二引数に正の整数を入れると
   //先頭から指定した個数だけ分割します。
   //分割できる数以上の数を指定すると無視します。
   //この場合は、ドッペとパックンだけを切り出します。
   elm := s.Split([','],2);
   write('overload1:');writeln(s);
   //分割した要素を表示
   for s1 in elm do
   begin
     writeln(s1);
   end;

   writeln;

   //結合(念のため文字列を初期化)
   //s := '';
   //Writeln(s);

   //s := s.Join(',',elm);
   //Writeln(s);


   //オーバーロードの2つ目、第二引数に
   //TStringSplitOptions.ExcludeEmptyを指定すると
   //空文字を無視して切り出します。
   //TStringSplitOptionsを指定しない場合、あるいは
   //TStringSplitOptions.Noneを指定した場合は
   //空文字も1つとして切り出します。
   elm2 := s.Split([','],TStringSplitOptions.None);
   write('overload2-1:');writeln(s);
   for s1 in elm2 do
   begin
     writeln(s1);
   end;
   writeln;

   elm2 := s.Split([','],TStringSplitOptions.ExcludeEmpty);
   write('overload2-2:');writeln(s);
   for s1 in elm2 do
   begin
     writeln(s1);
   end;
   writeln;


   //オーバーロードの3つ目、第二引数に正の整数を入れると
   //先頭から指定した個数だけ分割します。
   //このときTStringSplitOptions.ExcludeEmptyを指定すると
   //空文字を無視して切り出します。
   //TStringSplitOptionsを指定しない場合、あるいは
   //TStringSplitOptions.Noneを指定した場合は
   //空文字も1つとして切り出します。
   //この場合はベルト・サタンも切り出します。
   elm2 := s.Split([','],4,TStringSplitOptions.ExcludeEmpty);
   write('overload3:');writeln(s);
   for s1 in elm2 do
   begin
     writeln(s1);
   end;
   writeln;

   //オーバーロードの4つ目、セパレータに文字列を指定する
   //こともできます。
   //この場合は、ドッペ,パッと,ニーナ,,ベルト・サタン,,キノッピー
   //に分割されます。
   //(セパレータにCRLFを指定することが可能です。)
   elm2 := s.Split(['クン'],TStringSplitOptions.None);
   write('overload4:');writeln(s);
   for s1 in elm2 do
   begin
     writeln(s1);
   end;
   writeln;


   //オーバーロードの5つ目、セパレータに文字列を指定したうえで
   //分割した結果を取り出すことも可能です。
   //この場合は、ドッペ,パッだけを取り出します。
   //に分割されます。
   elm2 := s.Split(['クン'],1,TStringSplitOptions.None);
   write('overload5:');writeln(s);
   for s1 in elm2 do
   begin
     writeln(s1);
   end;
   writeln;


   readln;


  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

で実際に実行した結果のビットマップが


です。

追伸:サンプルに使用した文字列がマイナーすぎたようなので、ちょっとだけわかりやすい
ものにしました。

2012年9月5日水曜日

SplitとJoin

Delphi XE3で新たに導入されたStringHelperを使ってみた。 使ったのはJoinとSplit。
それぞれ一番簡単な呼び出し形式です。
以下、プログラム



unit Unit1;
program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

var
  s : string;
  elm : TArray;
  //sh : TStringHelper;
begin
  try
    { TODO -oUser -cConsole メイン : ここにコードを記述してください }
   //sh : TStringHelper
   s := 'キャサリン,さをり,ツネアキ,ハゲミーナ,ヒデオ';
   Writeln(s);

   //カンマで分割
   elm := s.Split([',']);

   //分割した要素を表示
   for s in elm do
   begin
     writeln(s);
   end;

   //結合(念のため文字列を初期化)
   s := '';
   s := s.Join(',',elm);

   Writeln(s);
   readln;



  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

で実際に実行した結果が、

です。

2012年6月7日木曜日

DelphiでExcelブック内のシート一覧を取得し表示する(dbGo経由)

前のブログで、ExcelのTypeライブラリーを使ってシート一覧を取得しましたが
ついでといっては、なんですが、dbGo(Ado)を使って、シート一覧を取得してみます。

TAdoQueryを使ってSQL文で、テーブル一覧を取得できないか、ちょっと調べたましたが
無理そうだったので、ここでは、

TADOConnection.OpenSchema (
         const Schema: TSchemaInfo;  
        const Restrictions: OleVariant;
         const SchemaID: OleVariant;
                 DataSet: TADODataSet);

メソッドを使ってテーブルを取得します。

方法は、簡単で、

OpenSchema関数のパラメータ

     Schemaに TSchemaInfo.siTables
     DataSetに スキーマ取得結果の書き込み先のレコードセットを指定します。

      
     また、今回は、RestrictionsSchemaIDは使用しませんのでEmptyParamを指定します。


さて、やってみます。

フォームにTADOConnectionを配置し、ConnectionStringの

   ProviderにMicrosoft.ACE.OLEDB.12.0
   Data SourceにExcelのワークブックのパス
   Extended PropertiesにExcel 12.0(Excel2010の場合)

を指定します。

(ConnectionStringについては、http://connectionstrings.com/ が参考になります。)


次に結果格納先としての TADODataSetコンポーネントを配置し、Connectionプロパティに
上記の TADOConnectionコンポーネントを指定します。

あとは、通常の操作で、DataSource,DbGridを配置し、それぞれ接続します。

あとはボタンなどを配置しそのイベントハンドラに

  ADOConnection1.Connected := true;
  ADOConnection1.OpenSchema(siTables, EmptyParam, EmptyParam,ADODataSet1);
 

のようなコードを書きます。

で実行すれば、





のように結果が得られます(右側)

 
 
なお、torry's Delphiのページにもうちょっと詳しいサンプルがあります。http://www.swissdelphicenter.ch/torry/showcode.php?id=1433

また、AdoでのExcelのSchema,については、MSのHELP
http://support.microsoft.com/kb/257819/ja


が参考になります。

2012年6月3日日曜日

DelphiでExcelブック内のシート一覧を取得し表示する

先日、DelphiでExcelのWorkSheetを列挙しながらSheetを編集する処理を作成したとき、
思いもよらずはまったので、自分メモとして保存。

DelphiからExcelを操作する方法としては、

  1. Excelのタイプライブラリーをインポート
  2. dbGoを使用する
  3. サードパーティのコンポーネントを使用する
  4. ・・・
などの方法があげらるが、

今回は、1.タイプタイプライブラリーをインポートしてExcelを操作しシート名を一覧表示する
処理をつかった。

以下、ソース

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Excel_TLB,System.Win.ComObj;
const
  LCID = LOCALE_SYSTEM_DEFAULT;


procedure TForm1.Button1Click(Sender: TObject);
Var
  ExcelApp : Excel_TLB.ExcelApplication;
  ExcelBook : Excel_TLB.ExcelWorkbook;
  ExcelSheet : Excel_TLB.ExcelWorksheet;
  BookPath : String;
  i : integer;
begin
  ListBox1.Clear;
  ExcelApp := CreateComObject(CLASS_ExcelApplication) as ExcelApplication;
  ExcelApp.DisplayAlerts[LCID] := false;

  BookPath := IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + 'Test.xlsx';

  ExcelBook := ExcelApp.Workbooks.Add(BookPath, LCID);

  (* このように書きたいが
    'GetEnumerator' のメンバが含まれていないかアクセスできないため)
      通常では使用不可   
  for  ExcelSheet in  ExcelBook.Worksheets do
  begin
     ListBox1.Items.Add(ExcelSheet.Name);
  end;
  *)


  // Excelのコレクションは1基数なので1からカウントを始める。
  for i := 1 to ExcelBook.Worksheets.Count do
  begin
    ExcelSheet := ExcelBook.Worksheets.Item[i] As  Excel_TLB.ExcelWorksheet;
    ListBox1.Items.Add(ExcelSheet.Name);
  end;

  ExcelSheet := nil;
  ExcelBook.Close(false,BookPath,false,LCID);
  ExcelBook := nil;

  if Assigned(ExcelApp) then
  begin
    ExcelApp.Quit;
    ExcelApp := nil;
  end;


end;

end. 
for ~ in doの構文が使えると、基数のこと意識しなくても良いが、コンパイルすると

 E2431 for-in ステートメントはコレクション型 'Sheets' で動作できません('Sheets' に 'GetEnumerator' のメンバが含まれていないかアクセスできないため)

のメッセージが、出てEXEが作れないため、従来のfor文で列挙している。

  Excelのコレクションが1基数なので、for 文は、1からシート数まででにしていのが
 ポイントです。(ポイントというもののものではありませんが・・・)

 まあ、Excelに限らず、Win32版のVisual Basic(VB6,VB5)とか、VBAのコレクションInterface
 は基本1基数なのですが・・・

 下図のようなブックに対して





 上のような処理を実行すると

 のような結果がえられます。


以下は、余談ですが、

自分 、Excelのコレクションが1基数だということをすっかり忘れていて、結果、午前中つぶしちゃいました。

2012年3月5日月曜日

Unified Interbaseコンポーネントをつかってみた(番外編:Delphi Xe2で使う)

Unified Interbaseリポジトリには、Delphi Xe2用のパッケージ(プロジェクト)がありますので

Unified InterbaseのコンポーネントはDelphi Xe2にインストール可能です。

Xe2のバージョン管理リポジトリから開く機能を使って
としてソースをダウンロードし、
ダウンロード先、packagesフォルダから、UIBD16Win32.groupprojを開いて
ビルドすればインストールできます。

インストールしたコンポーネントは、

な感じになります。(64ビットもサポートされたてます。)

あとは、Unified Interbaseコンポーネントをつかってみた(その1)と同様にして
Firebirdと接続が可能です。

2011年9月24日土曜日

LiveBindingを試してみる。

DelphiXE2のLiveBindingの機能を使って、現在時刻を更新する処理を
チュートリアルを参考作ってみた。

処理としては、時刻が更新されると、登録した通知先(作った例場合はフォーム)の
表示を更新する処理になっています。

以下ソース

まずは、時計のソース、タイマーを使って定周期で時刻を更新し、登録先の
更新通知を行っています。
また、変更通知先を登録する処理を書いています。


unit Unit4;

interface

uses
System.SysUtils, System.Classes,Vcl.ExtCtrls, Data.Bind.EngExt,
Vcl.Bind.DBEngExt, System.Rtti, System.Bindings.Outputs, Vcl.Bind.Editors,
Data.Bind.Components,
System.Bindings.EvalProtocol,
System.Bindings.Expression,
System.Bindings.ObjEval,
System.Bindings.Helper;

type
TDataModule4 = class(TDataModule)
FTimer: TTimer;
procedure FTimerTimer(Sender: TObject);
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
private
{ Private 宣言 }
FNowString : String;
BindingExpression1: TBindingExpression;
public
procedure AddBindingList(const InputScopes: array of IScope; const BindExprStr: string; const OutputScopes: array of IScope; const OutputExpr: string);
published
{ Public 宣言 }
property NowString : String read FNowString;
end;

var
DataModule4: TDataModule4;

implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

uses Unit1;

{$R *.dfm}

procedure TDataModule4.AddBindingList(const InputScopes: array of IScope;
const BindExprStr: string; const OutputScopes: array of IScope;
const OutputExpr: string);
begin

BindingExpression1 := TBindings.CreateManagedBinding(
InputScopes,
BindExprStr,
OutputScopes,
OutputExpr,
nil);

end;

procedure TDataModule4.DataModuleCreate(Sender: TObject);
begin
//BindScope1.Active := true;
end;

procedure TDataModule4.DataModuleDestroy(Sender: TObject);
begin
//BindScope1.Active := false;
end;

procedure TDataModule4.FTimerTimer(Sender: TObject);
begin
FNowString := DateTimeToStr(Now);
TBindings.Notify(Self, 'NowString');

end;

end.


次に、時計を表示するソース。比較のためにポーリング処理で上記のソースのプロパティを使って
タイムスタンプを更新する処理もあります。


unit Unit1;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
Data.Bind.EngExt, Vcl.Bind.DBEngExt, System.Rtti, System.Bindings.Outputs,
Unit4,
System.Bindings.Expression,
System.Bindings.ObjEval,
System.Bindings.Helper,
Vcl.Bind.Editors, Data.Bind.Components;


type
TForm1 = class(TForm)
Label1: TLabel;
Label3: TLabel;
Timer1: TTimer;
Label2: TLabel;
Label4: TLabel;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private 宣言 }
FSakaClock : TDataModule4;
public
{ Public 宣言 }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//FSakaClock.Free;
FSakaClock.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
FSakaClock := TDataModule4.Create(Self);
FSakaClock.AddBindingList(
{ inputs }
[TBindings.CreateAssociationScope([
Associate(FSakaClock, 'I1')
])],
'I1.NowString',
{ outputs }
[TBindings.CreateAssociationScope([
Associate(Label3, 'O1')
])],
'O1.Caption');

//FSakaClock := TSakaClock.Create(Self);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Label1.Caption := FSakaClock.NowString;
end;

end.