2008年12月23日火曜日

第102回目 やはりエディタは重要(その2)

○第102回目 やはりエディタは重要(その2)

 質問です。前回紹介のマクロはどのくらい便利なのでしょうか。
 名前はなんというのでしょうか。

 名前は、"どこでもコピデル"といいます。コピー等の編集機能のマクロとしては、まあまあ使えると思っています。

 まず、ちょっとなあという点から。

□このマクロでにおける違和感は、二回目以降の処理の時です。コピーを例にとりますと、通常の操作では、あれをここにという発想になります。しかしながら、このマクロは、ここにあれをということになります。1回目は"ここに"の指定が省略されていますので気にならないのですが、2回目以降、ああそうだった、そうだった、となることがあるのです。
=⇒誤った操作をした場合ために、処理を初期化(回数を0とする)する機能をESCとして追加しました。処理区分はそのままです。
□次にあるのは、連続処理とってもほとんど1回か2回で終わってしまうことです。連続処理のメリットが感じられないなあ、ということです。
=⇒これはどうしようもありません。
□また、先頭位置の指定後に、終了させてしまうと、選択が残っているので、通常処理にするには、ESCキーを押す必要があるということがあります。キーが効かないということになります。それは選択状態で終わってしまったということが原因です。
=⇒終了する時に、選択状態である場合は、選択状態を解除するという処理を入れました。
□Shiftを押すと、最後に修正した箇所へ飛びますが、癖でShiftを押してしまうことがあります。すると、思わぬところへ移動してしまうので、元の場所がわからなくなることになります。Shiftの機能をはずすのも忍びないし…。
=⇒大きく移動する時(しばらくはShiftの場合に限定)には、移動前の場所を取得しておき、その場所に戻るという機能をF9として追加しました。デフォルト値は、貼付先とします。
=⇒見出し一覧から移動した場合、元の場所へに戻るというケースにも対応しました。
□これをあそこにコピーしたいといった時、いったんあそこにいってから、ここに戻ってくることになり、カーソル操作に無駄がでる。
=⇒コピーの逆指定機能をF8として追加しました。最初からでも逆指定できます。(やればできるものですね)
□クリップボードのある部分を貼付けられないか。つまり、QTCLIPを起動できないか。
=⇒通常通りのショートカットでQTCLIPを呼び出すことはできます。しかしながら、通常通りの操作では貼付まではできません。しかし、必要な文字列をクリップボードの先頭には動かすことができるので、その後それを貼り付けるという処理を行えばよいようです。クリップボードの内容を貼り付けるという機能をF4として追加できるかもしれません。
=⇒うまくいきません。QTCLIPの画面を出しておき、QTCLIPの中でマウスで必要な文字列を先頭に移します。QXエディタにもどりF4で貼り付ける、という方法が無難です。
□この機能を使えば、こういうことができるのでしょうか。このマクロの中でコピーしたものを複数回簡単に貼り付けること、ができる。どうなんでしょうか。
=⇒直前にコピーしたのはクリップボードの先頭にありますので、クリップボードからの貼付(すなわちF4)をすれば、それが貼付けられることになります。また、先頭になくても、前に紹介とたとおり、QTCLIPで該当文字列を先頭に持ってきて(マクロ中にできます)から処理を行えば、同様に複数回のコピーができます。
□直前のものだけではなく、前に貼付けたものも簡単に貼付けられませんか。
=⇒貼付けたものの履歴を取っておき、それを呼び出し、そこから指定したものを貼付けるという機能は面白いでしょうね。対応してみましょう。
□既存の文字にかぶせる、置き換えることはできますか。削除と一緒にできるといいですね。
=⇒難しそうですね。逆指定の中で何とかできるかもしれません。つまり、貼付先を指定するときに、範囲として指定するのですが、そうした場合、カーソルを改めて動かすことなく、貼付けると置き換わるのではないでしょうか。
=⇒だめですね、範囲の指定がいまのままだとできません。範囲の指定開始という情報を与えなければなりません。
 現状ではできないという整理にしておきます。
□削除の機能で、選択範囲が自動的に増えていくというのはどうでしょうか。適当なところで止めて微調整をし削除する、てな感じです、どうでしょうか。
=⇒面白いことを考えるなあという感想です。これ一辺倒というわけにはいかないので、区分する必要があります。DのかわりにFを押すと、この機能が動くというのどうでしょうか。止める時は矢印キーでもよさそうですね。そしてそれから通常の処理に戻して微調整をするということになるのでしょうか。プログラム上の表現としては、今のループに載せるか、もっと小さなループを作りまわしていくか。今のループの機能が複雑化するので、もっと小さなループを作るという選択でしょうか。
=⇒やってみました。確認できました。このことのノウハウで、見出し一覧から場所を指定する際の指示をenterキーで可能になりました。余禄がありました。
□それができるなら、コピーのときの範囲指定も同様にしてくれませんか。
=⇒徐々に要求が大きくなりますなあ。先ほどのものを基本に考えますと、現在のプログラムではそれほど複雑な処理になりません。通常処理での動作確認はしました。
□コピー元をどんどん蓄積していって、その累積を貼付というのはできませんか。
=⇒いろいろなところからもってきたものを、ここに貼付けるということになりますから用途はあると思います。この機能は別のマクロして整理しています。考え方が違うので、現在のマクロに入れるのは難しいと思っています。別のマクロを使ってくださいという整理でしょうか。多少は補助する機能として、前の場所にもどるという機能がありましたが、これで拡張します。貼付の後、そのあたりの場所で貼付位置を指定します。そして、F9をおすと、前のコピー元があった場所に移動するので、そこからコピー先を探しにいけることになります。少し一括貼付の一部を取り入れたことになるでしょう。
□日付とか、ファイル名とかを入力できませんか。短縮入力にそんなものが入っているんですが、短縮入力が使えればいいと思いますが。
=⇒短縮入力は3つ可能ですが、それらすべてということになると、入力値が多くなりますので、ひとつのみということで対応しましょう。いろいろなところから必要な文字列を引っ張ってこれるというコンセプトを満たしています。F6キーに設定してみます。
=⇒短縮入力の画面は出るのですが、その後の処理ができない。マウスも効かない。見出し一覧の場合とは違う。したがって、対応不能。
□さらに、あのファイルのものが使いたいが、開いていない。新しいファイルを開いてその中のものを使えないか。
=⇒右のエリアにファイルの履歴がありますが、それを開くのはマウスで可能なので開けるのではないかと思います。そのファイルに制御を移すために、ウィンドウのハンドル名を再取得する必要があります。
=⇒マウスは効きません。対応不能なので、マクロを終了させ、該当ファイルを開き、改めでマクロを実行してください。
□しおりの役目として、途中でスペースを入れたいと思う時があるのですが。
=⇒確かに、後で修正したいなというような場所、挿入した箇所をはっきりさせたい場所に、それとわかる記号を入れておきたい時がありますね。スペースがいいのですかね。もっとはっきりさせるために改行はどうでしょうか。いろいろとキーを使っていますので、あまり使えるキーが少なくなっているというのが現状です。Ctrlを押すことによって改行が挿入されることにしましょうか。
※最終形のマクロは各自の課題として内容掲載は省略します。

2008年12月14日日曜日

第101回目 やはりエディタは重要

○第101回目 やはりエディタは重要
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデータ以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 第90回から9回ほどエディタでのマクロを紹介しましたが、使っていますと、その重要性を痛感します。
 事務屋としての仕事の半分ぐらいは、エクセルではなく文書の作成の人も多いのではないでしょうか。エディタは文書の体裁という面では弱いのですが、文書の内容を作るという面では非常に活用度が高いと思います。
 そんな思いがつのりますので、紹介したQXエディタマクロの機能拡大を紹介します。
 文書の編集としてのコピー等の機能です。第93回と第97回のものを中心にしています。

大まかな機能
 コピー(移動を含む)と削除を切り替えられる(初期値はコピーとします)。
 一字一字の削除は、処理区分にかかわらずdeleteキーでできる(下の場合を除く)。
 先頭位置の指定後のdeleteキーは範囲指定の終了と見て指定範囲を削除する。
 1回の処理ではなく、連続して処理ができる。
 ウィンドウの移動ができる。
 見出し一覧を表示し、そこから見出し単位で移動できる。
 タスクバーに現在の処理の状態の表示がでる(コピーか移動か削除か。貼付位置の指定か、例えばコピー文字列の先頭の指定か、最後の指定か)。=⇒これは本論ではないので、簡素化するために省略します。

【キーの操作内容】
 KEY_RETURN、KEY_SPACE=⇒場所の確定
 KEY_PRIOR =⇒上に大きく画面を動かす
 KEY_NEXT =⇒下に大きく画面を動かす
 KEY_UP、KEY_DOWN、KEY_LEFT、KEY_RIGHT =⇒上下左右にカーソルを動かす
 KEY_HOME、KEY_END=⇒文頭、文末にカーソルを動かす
 KEY_TAB KEY_BACK =⇒単語単位で左右にカーソルを動かす
 KEY_MULTIPLY =⇒コピー・移動の切替指示
 KEY_SUBTRACT =⇒なし
 KEY_F12  =⇒処理の終了
 KEY_F1   =⇒ウィンドウを左に移動
 KEY_F2   =⇒ウィンドウを右に移動
 KEY_DELETE =⇒一字削除。先頭位置が指定されている場合は、範囲の確定及びその範囲を削除
 KEY_SHIFT =⇒直前に修正した箇所に移動
 &H43(C)   =⇒削除からコピーに処理を切り替える
 &H44(D)   =⇒コピーから削除に処理を切り替える
 &H4D(M)   =⇒見出し一覧を表示する
 
 流れです。
 1.ウィンドウのハンドル名の取得と、現在のウィンドウの番号の取得
 2.最初のカーソル行、場所の取得。使用するキーの指定。初期値の設定
 3.大きなループ。F12が押された場合、終了
 4.小さなループ。コピー、移動、削除。処理を行った後は指定回数を初期化する。

 1.ウィンドウのハンドル名の取得と、現在のウィンドウの番号の取得
  '現在のウインドのハンドル名を取得する
   gen_hwnd = @hwnd
  '全ウインドのハンドル名を取得する
  j = 0
  hwnd[j] = @@GetHwnd(2)
  do while hwnd[j]<>0
      j = j + 1
      hwnd[j] = @@GetHwnd(0)
      if hwnd[j] = gen_hwnd then hwnd_no=j  '現在のウィンドウ番号を取得
  loop
  '全ウインドのハンドル名の数
   hwnd_cnt = j

 2.最初のカーソル行、場所の取得。使用するキーの指定。初期値の設定
 'カーソル行及び場所(バイト数)を記憶
 y_begin = @ScrLineToCrLine(@Line)   '物理行から論理行への変換
 
p1=@BytePosCr
'キーの設定
 call iskeypressed(KEY_RETURN)
 call iskeypressed(KEY_SPACE)
 《省略。【キーの操作内容】参照》
 call iskeypressed(&H44)
 call iskeypressed(&H4D)  'M
'初期値設定
 j = hwnd_no  'ウィンドウの移動は、最初は現在のウィンドウを基準とする
 flag = 1   '初期状態では、貼付場所が設定されているので、1回の指定がなされているという意味
 kubun$ = "1" 'コピーということ
 fugou = 1   '終了処理に利用

 3.大きなループ。F12が押された場合、終了
 do
  小さなループ
 loop while 1

 4.小さなループ。コピー、移動、削除。処理を行った後は諸情報を初期化する。
 どのキーが押されたのかは、3行目のようにif文でiskeypressed(KEY_RETURN)というふうにきいていく。
 do
 call _sleep(50) '無限ループなので、これがないとCPUが回りっぱなしになってしまいます。50とは千分率で、0.05秒休むという意味です。
 if iskeypressed(KEY_RETURN) or iskeypressed(KEY_SPACE) then
  if kubun$ <> "3" and flag=2 then exit do
  if kubun$ = "3" and flag=1 then exit do
  if kubun$ <> "3" and flag=0 then
   y_begin = @ScrLineToCrLine(@Line)   '物理行から論理行への変換
   
p1=@BytePosCr
   gen_hwnd = @hwnd  '現在のウィンドウのハンドル名を取得
  elseif kubun$ = "3" and flag=0 then   '削除処理で何も指定していない時
   y_begin = @ScrLineToCrLine(@Line)   '物理行から論理行への変換
   
p1=@BytePosCr
   gen_hwnd = @hwnd
   @BlockSelect
   FOR i=1 to ll005
    @MoveRightChar
   next
  else
   @BlockSelect
  end if 
  flag++
 elseif iskeypressed(KEY_DELETE) then
  if kubun$ = "3" and flag=1 then exit do
  @DeleteChar
 elseif iskeypressed(KEY_F12) then
  fugou = 9  '終了
  exit do
 elseif iskeypressed(KEY_MULTIPLY) then  '*が効く
  if kubun$ = "1" then
   kubun$ = "2"  '移動
  else
   kubun$ = "1"  'コピー
  end if
 elseif iskeypressed(KEY_F1) then  'ウィンドウを左に移動
  if kubun$ <> "3" or flag <> 1 then '削除で1回指定後は移動できない
   j = j + hwnd_cnt - 1
   j = j mod hwnd_cnt
   @@Activehwnd hwnd[j]
  end if 
 elseif iskeypressed(KEY_F2) then  'ウィンドウを右に移動
  if kubun$ <> "3" or flag <> 1 then '削除で1回指定後は移動できない
   j = j + 1
   j = j mod hwnd_cnt
   @@Activehwnd hwnd[j]
  end if 
 elseif iskeypressed(KEY_SHIFT) then
  @MoveLastModify  '最後の修正があった場所に移動。意味が薄い。
 elseif iskeypressed(&h43) then  'C
  if kubun$ = "3" and flag = 0 then  '3は削除のこと。ここでは、何も指定されていない時に、Cが押された。
   kubun$ = "1"
  elseif kubun$ = "3" and flag = 1 'ここでは、削除中で、先頭が指定されたいる時に、Cが押された。
   kubun$ = "1"
   @BlockSelect  '選択を解除するという意味。
  end if
 elseif iskeypressed(&h44) then  'D
  if kubun$ <> "3" and flag = 0 then
   kubun$ = "3"
  elseif kubun$ <> "3" and flag = 1 then
    kubun$ = "3"
    @BlockSelect
    FOR i=1 to ll005
    @MoveRightChar
   next
  end if
 elseif iskeypressed(&h4D) then  '見出し一覧表示
  @ListCaption
 elseIf iskeypressed(KEY_PRIOR) then
  @ScrollUpHalfPage
 elseif iskeypressed(KEY_NEXT)  then
  @ScrollDownHalfPage
 elseif iskeypressed(KEY_LEFT)  then
  @MoveLeftChar 
 elseif iskeypressed(KEY_UP)  then
  @MoveUpChar
 elseif iskeypressed(KEY_RIGHT) then
  @MoveRightChar 
 elseif iskeypressed(KEY_DOWN)  then
  @MoveDownChar
 elseif iskeypressed(KEY_END)  then 
  @MoveEndLine
 elseif iskeypressed(KEY_HOME)  then
  @MoveBeginningLine
 elseif iskeypressed(KEY_TAB)  then  '逆にした。感覚的なもの
  @MoveLeftWord
 elseif iskeypressed(KEY_BACK)  then  '逆にした。感覚的なもの
  @MoveRightWord
 end if
 loop while 1
 if fugou = 9 then exit do '終了の場合
'指定範囲をクリップボードへ、クリックからコピー。削除は単純に削除する
 if kubun$ = "1" or kubun$ = "2" then
  if kubun$ = "1" then
   @BlockClipboardCopy 
  elseif kubun$ = "2" then
   @BlockClipboardCut 
  end if
  @@Activehwnd gen_hwnd   '貼付先のウィンドウに移動
  @Line = @CrLineToScrLine(y_begin)
  @BytePosCr=p1
  @BlockClipboardPaste
 else
  @BlockDelete
 end if
 flag = 0
(この後に大きなループの loop while 1が続く)

2008年12月6日土曜日

第100回目 複数ファイルの処理

○第100回目 複数ファイルの処理
 複数ファイルを対象としたマクロとしては、『第89回目 ブック(ファイル)を超えての処理の仕方』でフォルダー内のエクセルファイルに対して、置き換えの処理をするという、ひとつの形を紹介しました。
 ここでは、同一フォルダという制約を取り外してものを考えます。すなわち、与えられたフルパス名のファイルを処理対象にします。処理内容は、ある範囲のデータを集めるという処理にしました。データを集約するファイルは、別のものとなります。そのファイルを開いておいて、フルパス名、シート名、シート上のタイトルのある座標、データの座標範囲、貼付先の先頭座標をセルに書き込んで、その範囲を指定してから実行とします。
 簡単にするために、コピー元のファイルは同一形式になっているので、シート名、シート上のタイトルのある座標、データ範囲の座標はすべて同じとします。(ここを変えたいものにするにはセルにその情報を書きこんでおいて、その情報を使用してください。複雑度は増しますが、原理的には簡単だと思います。注意、貼り付ける列の数の取得も忘れずに)

 流れです。
 1.指定された範囲から、フルパス名(コピーもとのファイル、複数)、シート名(同一としたので一つ)、シート上のタイトルのある座標(同一としたので一つ)、データの座標範囲(同一としたので一つ)、貼付先での先頭ファイル用の貼付け座標(順次自動的にずらしていくので一つ)を取得します。ここで、ファイルの数を求めます。
 2.指定されたファイルを開き、シートを指定して、指定されたタイトルをコピーします。
 3.貼付先のファイルに移動し、貼付先の2行目(列はデータと同じ)に貼付ます。
 4.指定されたファイルに移動し、指定された範囲のデータをコピーします。処理が終わったらファイルは閉じておきます。
 5.貼付先のファイルに移動し、貼付先の先頭座標に貼付ます。このときは、値のほかに書式も貼り付けますので、貼付は2回となります。
 6.次のファイルを開いて、2から5をファイルの数だけ繰り返します。
  2回目以降は、貼付先の先頭座標が、貼付けた列の数だけ変わりますので注意してください。

 1.指定された範囲から、フルパス名、シート名、データの座標範囲、貼付先の先頭座標を取得します。ここで、ファイルの数、コピーするデータの個数を求めます。
 先頭座標から下に向かって、フルパス名を取得します。
 シート名以下は、二つ隣の列にはいっています。
 準備処理として、最初のファイルの名前、シート名を取得しておきます。
  Application.DisplayAlerts = False '警告・確認メッセージを表示しない
F_name = ActiveWorkbook.Name
sh_name0 = ActiveSheet.Name

'フルパスファイル名、シート名(一つ)、シート上のタイトルのある座標、コピー元の座標(一つ)、貼り付け座標(一つ)を取得する
  gyo1、retu1は指定した範囲の先頭の座標です。
For i = 1 To 100
If Cells(gyo1 + i - 1, retu1) = "" Then Exit For
FName(i) = Cells(gyo1 + i - 1, retu1)
Next
f_cnt = i - 1 'ファイル数
'シート名(一つ)以下の項目
sh_name = Cells(gyo1, retu1 + 2)    'シート名
title_A1 = Cells(gyo1 + 1, retu1 + 2) 'タイトルの座標
moto_A1 = Cells(gyo1 + 2, retu1 + 2)  'コピーもとのデータの範囲座標
saki_A1 = Cells(gyo1 + 3, retu1 + 2)  'データの貼付先の先頭座標。タイトル、ファイル名もうちますので、十分下に設定してください。
  '貼付けるデータの列数
p1 = InStr(moto_A1, ":")
p2 = Left(moto_A1, p1 - 1)
p3 = Mid(moto_A1, p1 + 1)
retu11 = Range(p2).Column
retu12 = Range(p3).Column
retu_cnt = retu12 - retu11 + 1  '貼付ける列の数
  '貼付先の座標の列番号、行番号化
retu21 = Range(saki_A1).Column
gyo21 = Range(saki_A1).Row

 2 - 6の処理
'ファイルを開き、文字列を変換し、ファイルを閉じる。メッセージは表示させない。
 ll000 = 0 'コピーした間に空白列をおく場合は、1以上を入れる
For i = 1 To f_cnt
'ファイルを開く
Workbooks.Open Filename:=FName(i)
F_name1 = ActiveWorkbook.Name
Worksheets(sh_name).Select
Range(title_A1).Copy
'コピー先ファイルに戻り、タイトル(1行目)、ファイル名(2行目)を貼り付ける
Windows(F_name).Activate
Worksheets(sh_name0).Activate
Cells(1, retu21 + (i - 1) * (retu_cnt + ll000)).PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Cells(2, retu21 + (i - 1) * (retu_cnt + ll000)) = F_name1
'コピー元ファイルに戻り、データを取得する
Windows(F_name1).Activate
Worksheets(sh_name).Select
Range(moto_A1).Copy
'先ファイルに戻り、データを貼り付ける
Windows(F_name).Activate
Worksheets(sh_name0).Select
Cells(gyo21, retu21 + (i - 1) * (retu_cnt + ll000)).PasteSpecial Paste:=xlPasteValuesAndNumberFormats, Operation:= _
xlNone, SkipBlanks:=False, Transpose:=False
Cells(gyo21, retu21 + (i - 1) * (retu_cnt + ll000)).PasteSpecial Paste:=xlPasteFormats, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
'コピー元ファイルに戻り、ファイルを閉じる
Windows(F_name1).Activate
ActiveWorkbook.Close SaveChanges:=True
Next
 Application.DisplayAlerts = True '警告・確認メッセージを表示する

 ※実際の業務で行った例では、ファイル数は23個でした(1グループあたり)。数が多いことが考えられをますので、ファイルのフルパス名の簡単な取得も考えておくべきだと思います。筆者はエクスプローラは通常使っていませんので、どのようにスムーズに取得するのか不明です。(筆者の使っているファイラーでは、一覧表からキーボードでファイルを指定(スペースキー)して、Ctrl+Cでそれらのファイルのフルパス名がクリップボードにコピーされ、それをエクセルに貼付ければ終わりになります)
 ※データを貼付後、23を4つにわけ集計することになりました。集計項目は人数と金額1,2(ともに平均)でした。問題を簡単にして、人数、金額1,2(ともに平均)がその順番で横に23個つながっています(縦項目は所属で50個あります)。たて項目ごとに4つに集約するには、どうしたらスムーズにいくのかというテーマは頭の体操となるものと思われます。(実際のやり方は、地道に加重平均を取る計算式を作っていけばいいのですが、スムーズに作成するにはどうしたらいいのかということがテーマです。平均して6個(23/4)の加重平均となります。地道にやればできますが結構大変です)


 ※今回紹介したマクロは、非常に自動化の効果がありますので、違うシート、違うデータ範囲のものにまで一般化しておくと、いいと思います。

2008年11月23日日曜日

第99回目 計算式の加工("#DIV/0!"対応、端数処理とif文)

○第99回目 計算式の加工("#DIV/0!"対応、端数処理とif文)

 計算式は、一般が難しいのでなかなかマクロの対象にはなりません。しかしながら、一部に基本となる計算式を加工するという例があり、それはある程度一般化できそうです。
 ここでは、Round関数と"#DIV/0!"対応を考えてみます。
 CJ11*CK11/CI11、CJ11*CK11/(CI11+CH11)というような式があったとします。
 これらの式の問題点は、分母がゼロになった場合の"#DIV/0!"エラーが出てしまう。端数処理がされていないというものです。
 この二つを何とかしようというマクロです。
 計算式を作る最初から、この二つ問題点が出ないように組立てることは可能ですが、なんせめんどくさいです(これは人によって違うと思います)。
 なお、割り算が入っていないとしても端数処理は必要です。

 上の計算式は、いってみれば核となるものです。これだけは確実につくらなければなりません。
 そこからの加工として何か有効なマクロはないか、という問題意識です。
 上の一番目の式の場合、"#DIV/0!"対応としては、大体の人が次のようにしているのではないでしょうか。
  =if(CI11<>0,CJ11*CK11/CI11,"") または
  =if(CI11<>0,CJ11*CK11/CI11,0)
 ここに端数処理をつければ、
  =if(CI11<>0,ROUND(CJ11*CK11/CI11,3),"")
 となります。
  =if(CI11<>0,ROUND(  CJ11*CK11/CI11  ,3),"")
 と三つに分けると、処理内容が見えてきます。
 しかし、変動項目があります。if文のCI11<>0のCI11と端数処理値の3です。
 前者は中から持ってくるとして、3は入力値として与えます。
 前者は、/の後の部分と勝手解釈してもよさそうです。

 流れは次のようになります。
 1.選択範囲を取得する
 2.端数処理値を入力する
 3.選択範囲内のあるセルの計算式を取得する
 4.分母を切り出し取得する
 5.if文及びROUND文を作る
 6.該当セルに加工した計算式を埋め込む
 7.選択範囲のセルについて繰り返す

ここで気になるのは、加工した計算式が、計算式としてエラーであった場合、どこの段階で止まりどうなるのかということです。
=⇒マクロの中で加工した計算式をセルに埋め込む時に止まる、ということでした。

それはさておき、
 第51回目に次のような記述があります。
'範囲を指定して実行する。
'計算式のあるセルの計算式に特殊な記号を付加して文字化する
 Dim CCC As Object
 For Each CCC In Selection
   If CCC.HasFormula = True Then
     CCC = "xxx" & CCC.Formula
   End If
 Next
この辺を使って組み立てましょう。1と7はこれで十分ですね。
2の端数処理値の入力も説明を省略してもいいでしょう。

if文のなかを拡充していきましょう。
 3.選択範囲内のあるセルの計算式を取得する
AAA = CCC.Formula
 4.分母を切り出し取得する
p1 = InStrRev(AAA, "/")
bunbo = Mid(AAA, p1 + 1)
 5.if文及びROUND文を作る
AAA = "=if(" & bunbo & "<> 0," & "ROUND(" & Mid(AAA, 2) & "," & p3 & "),"""")"
 6.該当セルに加工した計算式を埋め込む
CCC.Formula = AAA

※InStrRev(AAA, "/")は、後ろから見ていき、/がある(先頭からの)場所を求める関数
 p3は端数処理値。
 "),"""")" は、『),"")』のこと。"を文字として使う時は、""と二つ重ねて初めて一つの"になる、と解釈される。
計算式としての条件を満たさない場合の対応方法としては、6の段階で、CCC = "xxx" & AAA として、処理後に文字化計算式を計算式にする処理をしてください。

 同様の処理として、if文作成があります。
 これは核となる計算式を、一例としては、「if( =<> ,」と 「," ")」とかではさむというものです。この場合は、計算式としての条件を満たしませんので、頭にxxxを付加して文字化計算式にしてください。
 これはネスト回数を入力することにすると、多少は利用度が広がります。


2008年6月22日日曜日

しばらく中断(第99回の一歩手前)

○しばらく中断(第99回の一歩手前)

 ここまで98回ほどやってきましたが、よくここまで題材があるという感じを持っています。
一方で、突っ走ってきた感じもしますので、これまでのことを整理したいと思います。後から見れば置き換えるべきところ、整理すべきところがありますので、これまでの内容をざっと見直していきたいと思っています。
 そのために、しばらく中断します。
 題材のほうはまだまだあるのですが、しばし、前に戻って。
 「伸びる前に屈せよ」

 ということで、しばらくは、これまでの整理の過程で気のついた点があれば、不定期的に載せていきたいと思います。

2008年6月14日土曜日

第98回目 QXマクロの紹介(その9)

○第98回目 QXマクロの紹介(その9)
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデータ以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 13.あの行をコピーして、中を逆転させればいいのではないか(特にマクロでは、"="の前後を変えたいということがよく起こります)
 14.前後の行を逆転させたい。一行の移動も一種の逆転でしょうか。含めましょう。

 13.あの行をコピーして、中を逆転させればいいのではないか(特にマクロでは、"="の前後を変えたいということがよく起こります)

 どの文字を区切りとするかの指定が必要です。初期値としてはTABを設定します。
 複数行を対象にできたほうが便利かと思います。

 処理の中心部分は非常に簡単です。
 1.指定範囲の行数を取得する
 2.区切り記号を入力する
 3.行の内容を取得
 4.区切り記号がある場所を探す。
 5.区切り記号より後ろの文字列 & 区切り記号 & 区切り記号より前の文字列
 6.新しい文字列を書き込む。
 7.既存の行を削除する
 8.これを指定範囲内の行で行なう

 1.指定範囲の行数を取得する
 定番の処理です。
'選択範囲を記憶
y_begin = @SelectStartLine
y_end = @ScrLineToCrLine(@SelectEndLine) 'これは論理的行
@BlockSelectEnd
if y_begin = 0 then
y_begin = 1
y_end = @ScrLineToCrLine(@LastLine) 'これは論理的行
end if

 2.区切り記号を入力する
'指定文字の入力
msg$ = "区分となる指定文字列をいれてください;タブの場合は0"
in1$ = inputbox$(msg$,"QX 文字列の逆転","0")
if in1$ = "0" then
rr$ = chr$(&H 9)  'TABのこと
else
rr$ = in1$
end if
in11 = len(rr$)

 3.行の内容を取得
   a$ = @TextCr$(@Line)

 4.区切り記号がある場所を探す。
   p1 = instr(a$,rr$)

 5.区切り記号より後ろの文字列 & 区切り記号 & 区切り記号より前の文字列
if p1 <> 0 then
mm1$ = left$(a$,p1-1)
mm2$ = mid$(a$,p1+in11)

 6.新しい文字列を書き込む。
@MoveBeginningLine
@Insert mm2$
@Insert rr$
@Insert mm1$
@InsertF "\n"


 7.既存の行を削除する
@DeleteLineCr
@MoveUpChar
end if

 8.これを指定範囲内の行で行なう
do
 :
@MoveNextLineCr
loop while @ScrLineToCrLine(@Line) <>


 14.前後の行を逆転させたい。一行の移動も一種の逆転でしょうか。含めましょう。

 入力値を3とすれば、現在行を含めて下3行を対象にし逆転するという意味とします。
 入力値を*3とすれば、現在行を含めて3行下の行を現在行の上に移動するという意味とします。
 まず、入力値を分解し、逆転か移動かの区分、対象行数を取得します。行数の初期値としては、2が便利です。2行逆転はよく使います。
 次に、対象行を配列変数に格納します。最大20行もあればいいでしょう。
 現在行から見て、入力値の分だけ格納します。削除も同時に行います。
 挿入は、逆から行ないます。以下参照
 '読み込み処理。
for i = 0 to p1-1  'p1は入力された行数
mm$[i] = @TextCr$(@Line)
@DeleteLineCr '当該行を削除
next
'コピー
for i = p1 to 0
@Insert mm$[i]
@InsertF "\n"
next

 一行移動の場合も、読み込み処理までは同じで、コピーするときに、2,1,0と書き込む代わりに、2,0,1とすれば十分かと思われます。

 今のは下の行に対してですが、上の行に対しても考えてみてください。入力値は-3、*-3とかにします。
 成功を祈ります。

2008年6月7日土曜日

第97回目 QXマクロの紹介(その8)

○第97回目 QXマクロの紹介(その8)
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデータ以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 QXはタブ形式のテキストエディタです。その一つ一つのタブを一つのウィンドウといいます。
 最高20まで開くことが出来るそうです(やったことはありませんが…)。
 あるウィンドウのある場所を、違うウィンドウのある場所にコピーする、というのが今回のテーマです。
 ウィンドウはどのようにして特定されているのか、その特定されたコードをどうやって全部取得することができるのか。今のウィンドウはどこに対応するものなのか。
 結果的には簡単でした。

'現在アクティブになっているウインドのハンドル名を取得する
gen_hwnd = @hwnd
'全ウインドのハンドル名を取得する
 j = 0
 hwnd[j] = @@GetHwnd(2) '表示されているタブの順番にハンドル名を取得という宣言で、一番目のハンドル名を取得
if hwnd[0] = gen_hwnd then j0=0 '現在
アクティブになっているウィンドウ番号を取得
 j = 0

 hwnd[j] = @@GetHwnd(2) '表示されているタブの順番にハンドル名を取得という宣言で一番目のハンドル名を取得
 do while hwnd[j]<>0  '二番目以降のハンドル名を取得
j = j + 1
hwnd[j] = @@GetHwnd(0)
if hwnd[j] = gen_hwnd then j0=j '現在アクティブになっているウィンドウ番号を取得
 loop
hwnd_cnt = j  '開かれているウィンドウ数
※ハンドル名はまったく意味不明な数値です。

 現在のウィンドウを選択
 j=j0
 @@Activehwnd hwnd[j]

 ウィンドウの移動
call iskeypressed(KEY_F1)
call iskeypressed(KEY_F2)
  :
  :
elseif iskeypressed(KEY_F1) then 'ウィンドウを左に移動
j = j + hwnd_cnt - 1 '左へ移動
j = j mod hwnd_cnt  'ウィンドウ数で割った余りでウィンドウを選択
@@Activehwnd hwnd[j]
elseif iskeypressed(KEY_F2) then 'ウィンドウを右に移動
j = j + 1
j = j mod hwnd_cnt
@@Activehwnd hwnd[j]
  :

 ここからは、ウィンドウ内の処理ですから、矢印キー等で貼り付け場所を指定します。
 EnterまたはSpaceキーで確定です。
 そこに、貼り付けます。
 クリップボードへのコピー、クリップボードからの貼り付けは前回どおりです。

※これまでのものにウィンドウを渡り歩くという機能をつけると、処理の幅広がります。

2008年5月31日土曜日

第96回目 QXマクロの紹介(その7)

○第96回目 QXマクロの紹介(その7)
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデータ以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 今回はこれを取り上げます。
 9.この文書の全文を別の文書のある部分にコピーしたい
 10.この文書のこの部分を別の文書のある部分にコピーしたい

 QXエディタはタブ形式です。ですから複数のファイルを一つのウィンドウで同時に開いておくことが出来ます。

 あるファイルのすべてを、別のファイルのカーソルのある位置にコピーします。トランザクションを別のファイルで作っておいて、完成後、メインのマスターに追加するという感じの処理というのでしょうか。

 意外とこれがスムーズに運びストレスを感じません。
 通常の作業で行なうと、
 1.Ctrl+A  すべてを選択
 2.Ctrl+C  コピー
 3.Ctrl+W(該当するウィンドウに移る。QXでは常に二つのウィンドウを認識している。それ以外の別のファイルへの移動は、Alt+W+番号となります)
 4.Ctrl+V  貼り付け
 です。
 これが同じ条件で、
 TAB+Aで終ります。(ショートカットキー化されています)

 本当に必要なのは一部をコピーすることだともいますが、これも比較すると
 範囲を指定し、TAB+X
 で終ります。(ショートカットキー化します)

 前者は後者に含まれますので、最終的には一つのマクロとなります。
 範囲が選択されていない=⇒全文コピー
 範囲が選択されている =⇒その範囲をコピー

 現在は、用途の関係から、それぞれ別マクロとし、更に後者を更に分けています。
 つまり、後者のほうを、
 範囲が選択されていない=⇒特殊記号に挟まれた部分をコピー
 範囲が選択されている =⇒その範囲をコピー
 としています。
 これは、辞書的なファイルを作っている関係から必要となったものです。
 一つの項目が○から始まり、次の○の直前で終わる。
 これを単位として、コピーするというものです。
 コピーしたい範囲内にカーソルがあれば実行できるので、非常に便利です。
 気持ちよくいく、という感性的な快感を重要視したものです。

 では単純な全コピーから。
if @hwnd = 0 then exit proc
@BlockSelectAll
@BlockClipboardCopy
@WindowChange 'QXエディタで現在認識されている別のファイルに移動
@BlockClipboardPaste
 でおしまいです。
 直前に表示されていたウインドのカーソルのあるところにコピーされます。

 では後者の場合です。
 まずは選択されているかどうかの判断です。
 '選択されているかどうかを取得
sel_md=@Select '0は選択されていません。
if sel_md=0 then

 選択されている場合は、
@BlockClipboardCopy 以下を実行すればいいことになります。

 選択されていない場合は、
 範囲を指定して、
@BlockClipboardCopy 以下を実行すればいいことになります。

 範囲指定は…。
 頭は、
'区切り語を上に向かって検索する。
@@FindAddString "^" & rr$,0  'rrは○です。"^"が付くことで先頭のものだけを検索します
@SearchRepeatUp
if @@SearchFound=0 then exit proc
'ブロック選択開始
@BlockSelect
 最後は、
'区切り語を下に向かって検索する。
@@FindAddString "^" & rr$,0
@SearchRepeatDown
'区切り語がなかった場合、行末にいく
if @@SearchFound=0 then
@MoveFileBottom
@MoveBeginningLine
end if
'クリップボードにコピー
@BlockClipboardCopy
 と続きます。

 感覚的に、操作がスムーズにいったという感じが必要だと思います。そのとおりの感じを受けるはずです。
 
 次に、機能拡大をすべき事項は、ウィンドウの選択です。現在は、直前表示されていたウィンドウに対して張り付けるということですが、これを、任意のウィンドウに、任意のカーソルの場所に貼り付けられるということです。
 いろいろ研究した結果、それほど難しくはないことがわかりました。
 次回、確認の結果、整理して紹介します。

※今回紹介したのは一つのかたちです。やっていくともっとということになり、それに対応していくと更に便利になります。
 考えられるアイデアとしては、区切り記号を入力するようにすることです(一部をメニュー化し、それ以外は入力することも考えられる)。これによって一つのエクセルマクロを簡単な操作でクリップボードにコピーすることができます(区切り記号はSubとなる。その後の操作は、エクセルのモジュールのウィンドウに移動して、Ctrl+Vで貼付けてください)。

2008年5月25日日曜日

第95回目 QXマクロの紹介(その6)

○第95回目 QXマクロの紹介(その6)
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデーター以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 8.複数の行のある位置に同じ文字列を挿入したい
 この機能もよく使います。
 特にマクロを作っているときに、デバック用に一時的にコマンド群を作ります。そしてチェックが終わるといらないのですが、後で何かの役に立つかということで、コメント分として残しておきます。また、あるマクロを元に新しいマクロを作るときなど、必要かもしれないというコマンド群をコメントとして残しておくことがあります。
 そのようなときに威力を発揮します。筆者は、コメント文にするというマクロと、それを通常のコマンドに直すというマクロを作っています(文頭に"'''"をつける、取るというマクロです)。
 マクロの行下げなどにも使います(タブを行頭に追加する)。
 挿入する文字列は指定できますので、いろいろと使い勝手がいいのです。
 また、文末に挿入することも可能です。この事例もあると便利です。
 しかしながら、途中に挿入する事例はありますが、非常に定式化できずらいと思います。

 流れです。
 0.対象となる行の範囲を指定します
 1.指定した範囲の行番号を取得
 2.挿入したい文字列の入力
 3.挿入する位置の入力
 4.挿入

 1.指定した範囲の行番号を取得
 これは定番の処理です。
  '選択範囲を記憶して、選択解除
  y_begin = @SelectStartLine
  y_end = @ScrLineToCrLine(@SelectEndLine)

 2.挿入したい文字列の入力
 '文字列の入力
msg$ = "挿入したい文字列を入力してください"
rr$ = inputbox$(msg$,"QX32 行頭に文字の挿入","\t") '\tはタブのこと
if rr$ = "" then exit proc

 3.挿入する位置の入力
'文字列の入力
msg$ = "挿入したい場所を入力してください。文字数"
a1 = inputbox(msg$,"QX32 行頭に文字の挿入",0)

 4.挿入
 挿入の対象行の制御はdo loopで行ないます。
  do
  :
   @MoveNextLineCr  '次の行に進める。この行は論理行である。
  loop while @ScrLineToCrLine(@Line) < a1 =" 0" a1 =""> len(a$) then
@MoveEndLine
@InsertF rr$ '制御文字(タブ、改行など)の可能性がありますので、@InsertFを使う。
else
b$=left$(a$,a1+1)
a2=lenb(b$)
@BytePosCr = a2
@InsertF rr$
end if
end if

 今回はここまで

2008年5月17日土曜日

第94回目 QXマクロの紹介(その5)

○第94回目 QXマクロの紹介(その5)
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデーター以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 削除の機能です。
 どの機能が必要かと思われますか。
 15、16あたりではないでしょうか(第90回参照)。
 15.一字一字削除していくのではなく、何文字か一気に削除できないか
 16.文末まで削除したい=⇒QXエディタの基本機能
 17.一行を簡単に削除したい=⇒QXエディタの基本機能
 18.何行かを一気に削除したい

 ワードでも、16は機能化されていますので、15を取り上げます。
 削除するとは…
 通常はその最初の場所にカーソルをもって行き、Delキーを必要なだけ押す、ということでしょう(範囲を設定してDelキーという方法もありますが、行をまたぐ大量の削除でなければ、それほどかわりません)。
 削除する文字数は最初からわかりますか?このあたりという感じは持っていたとしても、文字数はということになるとわからないのが普通ではないでしょうか。
 文字数を入力してEnterキーを押すというのは、一瞬時間が止まります。
 これをデメリットと考えるか、ワンタッチで処理が終了するのをメリットと捉えるか。
 これはいくら考えても解決しません。それほど大変ではないので、考えるよりは、作って好きなほうを使うという態度にしたらどうでしょうか。
 メリット面を生かすように、すでに初期値として何らかの数値が与えられている、としましょう。もし違っていたら…。道は二つに分かれます。あくまでの正確な数値を入力して自動的に削除してしまうという方法。もうひとつは、とりあえずの値を入力してから、通常の削除と同様に矢印キーを動かして指定する方法。
 後者のほうが無難ですね。
 もう一つ手抜きをして、マクロの中で適当な数の文字列を事前に設定して(入力はしないということです)、矢印キーで指定範囲を変えるということにします。
 削除範囲の確定はEnterキーです。
 初期値として何文字を指定すればいいのか。これはまったくわかりません。各自の事情に応じて変えてください。
 短めの場合は、5文字あたりでしょうか。
 長めの場合は、10文字あたりでしょうか。
 とりあえず筆者は、中間を取って8文字としました。

 整理してみますと、
 マクロを起動させますと
 8文字(初期値)の文字分が選択されています。EnterかDelキーを押すとその部分が削除されます。
 終わりの場所を変えたい場合は矢印キーで範囲を変えて、決まったところでEnterかDelキーを押す。
 という操作になります。
 このマクロでは文字列だけではなく、複数行も削除できます。
 ここまでくるとメリットが見えてきそうです。

 流れです。
 1.マクロを動かすと、カーソルのある文字から8文字が選択される。
 2.矢印キーで削除したい終わりにカーソルを移動する。
 3.範囲を確定し削除する。

 1.マクロを動かすと、カーソルのある文字から8文字が選択される。
'削除文字列の指定
@BlockSelect
FOR i=1 to p2  'p2には8を入れている
@MoveRightChar
next

 2.矢印キーで削除したい終わりにカーソルを移動する。
 これは前回の4.を参照してください。必要なキー操作だけ残してください。

 3.範囲を確定し削除する。
   do
   if iskeypressed(KEY_RETURN) or iskeypressed(KEY_SPACE) or iskeypressed(KEY_DELETE) then
   exit do
   elseIf iskeypressed(KEY_PRIOR) then
     :
     :
   end if
   loop while 1

   @BlockDelete

 こんな感じで終了です。
 有効なキーとしては矢印キーのほかに、Home、End、PageUp、PageDown、Tab(単語単位左へ)、BackSpace(単語単位右へ)あたりを、そろえておけばいいのではないでしょうか。
 よくわからないのですが、下矢印キーを1回押すと、その行全体が削除されます。
 上矢印キーを1回押すと、その行の上の行全体が削除されます。
 もっと続けると複数行が削除できます。
 これの変形で、下矢印キーをN回押す、左矢印キーを1回押すと、最初にあった文字から複数行が削除されます。これは最初にあった文字の前の部分は残るということです。

 ということで、複数行の削除まで出来てしまいました。
 PageUp、PageDownでは、7行分の指定が出来ますから、大きな範囲の削除も確認しながら簡単に出来ます。

 ※同一行の場合をよく使っています。使ってみて一言。
 結構便利です。ピタッとはまる時は感激します。

2008年5月10日土曜日

第93回目 QXマクロの紹介(その4)

○第93回目 QXマクロの紹介(その4)
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデーター以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 機能の拡張から根本的な改良
 1.前の文が短すぎて、入力で指定した位置に文字がない(本来は入力誤り)、カーソルの位置に文字がない(本来は指定誤り)の場合の処理が一つ考えられます。
 2.コピーもとの文字列を、直前の行だけでなく、当該行、直後の行も指定できる。更に、直前直後の行ではなく離れた行も指定できる、ということも考えられます。

 本質的な機能は後者です。前者は注意して入力すれば回避できますから。
 後者は、行の指定ですので、これまでは一行前だったので、最初の行のマイナス1でしたが、ここを変えればいいのです。最初の行を0として、前はプラスの数値、あとはマイナスの数値としていますので、
  前回の中で、
 『前の行の全文字列取得は、
  @Line = @CrLineToScrLine(y_begin-1) '論理行から物理行への変換
  b$ = @TextCr$(@Line)』
 としていましたが、(y_begin-1)の"1"を、入力したものにすればいいのです。
 これで終了です。

 簡単すぎてばかばかしいほどです。

 この機能を使って作業をしていますと、やはりこれもあれもということになっていきます。そして現時点の最終形は、最も簡単(マクロ構造上)なものに集約されてしまいました。
 基本的な違いは、カーソルの動きを主にしたことと、移動も加えたことです。
 前者の意味は、コピー元をカーソルを動かして先頭も最後も決定するということです。すべてカーソルを動かすのではなく、おおよその行の位置を入力します。入力値は第一次のカーソルの移動という意味合いとなります。その後にコピー元を決定しますので、間違った行であってもいいのです。(移動はコピーの変形なので詳細は省略)

 マクロの流れを追っていきます。
 1.カーソルの場所、行を取得する。最終的にはここに貼り付けます。
 2.およその行の入力。(移動の場合は頭に"*"をつけます)
 3.指定された行へカーソルを移動。横の位置は最初の位置のままとします。
 4.矢印キー等でコピー文の最初と最後を選択。選択はEnterとSpaceキーを使います。 矢印キーの外には、Home、End、PageUp、PageDown、Tab(単語単位左へ)、BackSpace(単語単位右へ)が使えます。
 5.コピー文をクリップボードに貼り付ける。(移動の場合は削除してクリップボードに張り付ける、ということが異なる)
 6.元の場所に戻ってクリップボードから貼り付け。

 1.カーソルの場所、行を取得する。
'カーソル行及び場所(バイト数)を記憶
y_begin = @ScrLineToCrLine(@Line) '物理行から論理行への変換
p1=@BytePosCr
 2.およその行の入力。(移動の場合は頭に"*"をつけます)
 入力値は、コピーの場合は、2、-2、移動の場合は*2、*-2。2は2行上、-2は2行下
 (入力値がヌルの場合は、コピー機能・当該行の選択とします)
 初期値は0、コピー機能・当該行の選択としました。
msg$ = "おおよその行を入力してください。移動の場合は頭に"*"をつける"
kubun$ = inputbox$(msg$,"QX32 指定文字列のコピー・移動","0")
if kubun$ = "" then
kubun$ = "1"
p3 = 0
else
p2 = instr(kubun$,"*")
if p2 = 0 then
p3 = val(kubun$)
kubun$ = "1"
ELSE
p3 = val(mid$(kubun$,p2+1))
kubun$ = "2"
end if
end if

 3.指定された行へカーソルを移動。横の位置は最初の位置のままとします。
'カーソルの大まかな移動
@Line = @CrLineToScrLine(y_begin-p3)
@BytePosCr=p1

 4.矢印キー等でコピー文の最初と最後を選択。選択はENTERとSpaceキーを使います。 矢印キーの外には、Home、End、PageUp、PageDown、Tab(単語単位左へ)、BackSpace(単語単位右へ)が使えます。
 4-1.使用するキーの指定
'キーの設定
call iskeypressed(KEY_RETURN)
call iskeypressed(KEY_SPACE)
call iskeypressed(KEY_PRIOR) '行を上に画面単位で移動
call iskeypressed(KEY_NEXT) '行を下に画面単位で移動
call iskeypressed(KEY_LEFT) '左矢印キー
call iskeypressed(KEY_UP) '上矢印キー
call iskeypressed(KEY_RIGHT) '右矢印キー
call iskeypressed(KEY_DOWN) '下矢印キー
call iskeypressed(KEY_END) '文末
call iskeypressed(KEY_HOME) '文頭
call iskeypressed(KEY_TAB) '単語単位に左に移動
call iskeypressed(KEY_BACK) '単語単位に右に移動
4-2.押されたキーによってカーソルを動かすなど
 押されたキーによって処理をする。
 マクロ中ですので通常のキー操作が効きませんので、押されたキーをもとにキー操作をマクロで作ります。
 確定は2回行ないますので、その区分をするためにフラッグを立てます。1回目は選択開始、2回目は選択の終了で、doループを抜け出して次の処理へ行きます。Spaceキーで文字を変換する操作になれているので、EnterキーのほかにSpaceキーでも可能としました。
 基本的には無限ループなので、CPUが使いっぱなしなってしまいます。途中に動きを止める命令(_sleep(50))を入れています。50とは1000分の50秒のことです。
 if文の後にあるコマンドは、QXマクロでのキー操作コマンドです。@MoveRightCharはカーソルを右に動かすコマンドです。
flag = 0
do
call _sleep(50)
if iskeypressed(KEY_RETURN) or iskeypressed(KEY_SPACE) then
if flag=1 then exit do
flag=1
@BlockSelect
elseIf iskeypressed(KEY_PRIOR) then
@ScrollUpHalfPage
elseif iskeypressed(KEY_NEXT) then
@ScrollDownHalfPage
elseif iskeypressed(KEY_LEFT) then
@MoveLeftChar
elseif iskeypressed(KEY_UP) then
@MoveUpChar
elseif iskeypressed(KEY_RIGHT) then
@MoveRightChar
elseif iskeypressed(KEY_DOWN) then
@MoveDownChar
【以下略】
end if
loop while 1
 5.コピー文をクリップボードに貼り付ける。(移動の場合は削除してクリップボードに張り付ける、ということが異なる)
 コピーか移動かはkubun$で判断します。
'指定範囲をクリップボードへ、クリックからコピー
if kubun$ = "1" then
@BlockClipboardCopy
else
@BlockClipboardCut
end if

 6.元の場所に戻ってクリップボードから貼り付け。
@Line = @CrLineToScrLine(y_begin)
@BytePosCr=p1
@BlockClipboardPaste

※比較
通常の操作(カーソルは貼り付けたい場所にある状態から。コピーもとが五行上にあったとする)
 1.カーソルを五行上に移動
 2.カーソルをコピー文の先頭に移動
 3.シフトを押しながらカーソルをコピー文の最後に移動
 4.Ctrl+Cでクリップボードへ
 5.カーソルを貼り付けたい場所に戻す。1と2の操作
 6.Ctrl+Vでクリップボードから貼り付け

 このマクロによる操作
 1.マクロの起動。TAB+1の2タッチ
 2.パラメータの入力。5と入力。
 3.入力値の確定。Enterキーを押す
 4.カーソルをコピー文の先頭に移動しEnterキーを押す
 5.カーソルをコピー文の最後に移動しEnterキーを押す

 共通部分を除いてみます。
通常の操作
 1.カーソルを五行上に移動
 3.シフトを押しながら
 4.Ctrl+Cでクリップボードへ
 5.カーソルを貼り付けたい場所に戻す。1と2の操作
 6.Ctrl+Vでクリップボードから貼り付け

 このマクロによる操作
 1.マクロの起動。TAB+1の2タッチ
 2.パラメータの入力。5と入力。
 3.入力値の確定。Enterキーを押す
 4.Enterキーを押す
 5.Enterキーを押す

 Ctrl+C、Ctrl+Vを2タッチとし、同数のキー操作を削除すると残るのは、

通常の操作
 1.カーソルを五行上に移動
 3.シフトを押しながら
 5.カーソルを貼り付けたい場所に戻す。1と2の操作

 このマクロによる操作
 4.Enterキーを押す
 5.Enterキーを押す

 通常操作では、1の操作が不要な場合もありますのでこの分を差し引いて、このマクロの効果としては、おおむね通常の操作の3、5が省略されるということといえます。

※掲載までの間での修正
 行数の入力をやめて、"3"を押すことによって3行移動させました。他の行数は面倒なので設定していません。
 2.パラメータの入力。5と入力。
 3.確定。Enterキーを押す
 の箇所が、
 3と上矢印と上矢印となります。キータッチとしては一つ増えました。また、下への場合は"-"を押す必要があるので、2タッチの増加です。しかしながら、感覚的に見て、こっちのほうがやりやすいようです。
 上矢印を5回押すよりも、3回(3と上矢印と上矢印)のほうが早いという主張です。
 質問
 なぜ3回なのか。=⇒PageUpでは7行動きます。真ん中で3か4かと考え、後は適当に3に決めました。
 3だけでなく、4、5、6も対象にしたらどうか。=⇒取り入れてみましょうか。1-9まで可能とします。

2008年5月4日日曜日

第92回目 QXマクロの紹介(その3)

○第92回目 QXマクロの紹介(その3)
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデーター以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 文章作成の効率を若干上げるマクロです。
 今回は、
 3.前の行のあの部分をコピーしたい
 を取り上げます。
 文字列の場所の指定としては、2-10(この10は文字数)、-10、15-、ここまでは普通でしょう。
 更に、0、0-12、0--10、99-10という指定もできるようにしています。
 0って?=⇒現在のカーソルの位置を基準として末尾までという指定です
 0-12って?=⇒現在のカーソルの位置を基準として後ろに12文字という指定です
 0--10って?=⇒同じく、現在のカーソルの位置を基準として前に10文字という指定です
 99-10って?=⇒文末から前に10文字という指定です
 (前の行が現在のカーソル位置よりも短い場合は、更に行を前にさかのぼっていって、その位置に文字がある行からコピーするようにしていますが、この講では、出来るだけ単純にするために、該当する文字列がなしとして、何もコピーしないことにします。)
 このマクロで問題になったのは、現在のカーソルの位置が、前の行の文字の位置とぴったり重なっていない可能性があるということです。半角全角が混じっていると、うまくぴったりあわないことがあります。
 それ以外の場合は、文字列の切り出し関数、mid$で開始文字数とコピーする文字数を指定すれば、簡単にコピーもとの文字列は取得できます。挿入する場所はカーソルのある場所の前になっているので、そのままま挿入すればいいことになります。
 これも、前回同様に処理の流れを見てみましょう。

 1.コピーしたい文字列の場所を入力する
 2.前の行を変数に取得する
 3.入力値を分解する
 4.位置の指定、カーソル位置からの指定、末尾からの指定ごとに処理を行なう。
  =前の行から文字列を切り出し、コピーする。

 1.コピーしたい場所を入力する
 msg$ = "コピーしたい文字列の範囲を入力してください。5-20、8-、-25、0、0-8、0--8、99-8"
 in_pos$ = inputbox$(msg$,"QX32 文字列のコピー","0")
 p2 = instr(in_pos$,"-")
 if p2 = 0 and in_pos$ <> "0" then exit proc
 -が含まれてなく、0でない場合は、終了とします(5と入力した場合、入力値は有効と思われますが、めんどくさかったので無効としました。1文字の場合は無理にでも、5-1のように指定をしてください)。

 2.前の行を変数に取得する
 これを行なうために、最初の行及びカーソルの位置を取得しておいてください。最初の行は論理行に変換します。
  y_begin = @ScrLineToCrLine(@Line) '物理行から論理行への変換
  p1=@BytePosCr
  前の行の全文字列取得は、
  @Line = @CrLineToScrLine(y_begin-1) '論理行から物理行への変換
  b$ = @TextCr$(@Line)
  となります。(この段階で、制御は前の行にいっていますので、貼り付ける時、もとに戻しておく必要があります)

 3.入力値を分解する
 -の位置が問題になります。これはp2に入っていますので、基本的にはその前後に分解します。前をm1、後ろをm9とします。分解した後は数字に直すことを忘れないようにしてください。m9は文字数という位置づけにします。まずは単純に分解してください。
 ただし、p2=0の場合("0"という入力の場合)は、m1は1、m9は999としておきます。
 ただし、p2=1の場合("-9"という形式)は、m1は1とします。
 ただし、p2=末尾の場合("8-"という形式)は、m9は999とします。
 (99-8の場合は、単純にm1は99、m9は8とします)

 3-2.指定値の修正
 前の行の文字数で修正します。最終的にはm1もm9も文字の位置にします。
 原則としてm9には文字数が入っていますので、m9=m1+m9-1とし、文字の位置にします。
 m1=99の場合は、m1="前の行の文字数"-m9+1とし、m9は"前の行の文字数(=位置)"とします。
 m9=999の場合は、m9="前の行の文字数(=位置)"とします。
 すると、もれるのは、カーソル位置から求める場合だけとなります。これはあとで記述。
 ただし、この結果、m1とm9が逆転していた場合は、m1とm9を入れ替えます。
 また、文字の長さをオーバーしていたとか、マイナスだったとか、文字の長さに制限する、1とするまたは処理終了(exit proc)する、とかしてください。

 4-1.位置の指定、末尾からの指定の場合
 カーソルを元の位置に戻します。
  @Line = @CrLineToScrLine(y_begin)
  @BytePosCr=p1
 コピーする文字は、
  c$=mid$(b$,m1,m9-m1+1)
 貼り付けは
  @Insert c$
  @BytePosCr=p1 'カーソルを元の場所に戻します。これはなくてもいいです。

 4-2.カーソル位置からの指定の場合
 最初のカーソルの位置を、p1=@BytePosCrで取得しています。
 前の行の文字のバイト長(lenb(b$))より、p1が大きかった場合は、処理を終了させます(exit proc)。
 p1は、カーソルのある文字が始まるバイト数となっています。頭から全角の文字列で4文字目にカーソルをおいた場合、カーソルの位置は7となります。前の行の7バイト目から始まる文字があるといいのですが、ない場合があります。最大が全角ですので、可能性は6バイト目から始まる全角文字です。(このような場合は、その文字から貼り付け対象とすることとします)
 さてさて、バイト位置と文字の位置をどうやって対応付けますか。
 1文字ずつ、そのバイト長を求めていきます。それを末尾まで行い、バイト数を累積したものを配列変数に入れます。
 これは、その文字の終わるバイト位置になっています。
 for i = 1 to len(b$)
  nn(i) = lenb(mid$(b$,i,1))+ nn(i-1)
 next
 さて、これらのバイト位置とp1を比較します。それぞれの意味合いが違うので注意を要します。
 前の例(7バイト目から始まっている)で考えて見ますと、6バイト目で終わっている文字があると、これは対象外です。次の文字はp1から始まっているということになりますから。
 順番に見て、7バイト目で終わっていた場合は、最初の文字としていいでしょう。
 順番に見て、7バイト目で終わっているものがなく、8バイト目で終わっていた場合は、7バイト目から始まっている全角の文字と判断できますので、最初の文字となります。
 順番に見て、9バイト目で終わっているものは、最大でも8バイト目から始まっているので、最初の文字とはいえません。
 したがって、初めて7バイトまたは8バイトの切れ目を持っている文字が、最初の文字となります。
for i = 1 to len(b$)
if p1 <= nn(i) then
m1=i
exit for
end if
next
次に、終わりの文字の処理です。
0-5とは、カーソルの位置以降の5文字という意味なので、
m9=m1+m9(5のこと)-1となります。(入力値を分解したものをまずm9に入れている)
0--5とは、カーソルの位置以前の5文字という意味なので、
m9=m1+m9(-5のこと)+1となります。(これは入力値がマイナスかどうかで判断できます。この結果、m1とm9が逆転しますので、入れ替えておいてください)
 m9が文字の長さをオーバーしてる場合は文字の長さにとどめておいてください。
 カーソルを戻し、文字を切り出し貼り付けるのは、4-1と同じですので省略します。

 ※機能の拡張
 前の文が短すぎて、入力で指定した位置に文字がない(本来は入力誤り)、カーソルの位置に文字がない(本来は指定誤り)の場合の処理が一つ考えられます。
 次に、コピーもとの文字列を、直前の行だけでなく、当該行、直後の行も指定できる。更に、直前直後の行ではなく離れた行も指定できる、ということも考えられます。
 ニーズいかんですが、必要性は結構あります(よ)。

 今回はここまで。

2008年4月27日日曜日

第91回目 QXマクロの紹介(その2)

○第91回目 QXマクロの紹介(その2)
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデーター以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 まず、
 3.前の行のあの部分をコピーしたい
 5.あの行(複数の行も可能とする)をコピーしたい(それをもとに修正すれば早い)
 を取り上げてみます。

 簡単なほうから
 5.あの行(全体)をコピーしたい(それをもとに修正すれば早い)

 この機能の意味はお分かりでしょうか。
 カーソルのある行を0とみたてて、前の行は、1、2、3…、後ろの行は-1、-2、-3…と数えることにします。
 0-3とした場合=⇒現在行を含めて前の4行を現在行の前の行にコピーします。
 2-3とした場合=⇒2行前と3行前の行を現在行の前の行にコピーします。
 3とした場合=⇒3行前の行を現在行の前の行にコピーします。
 後ろの場合はマイナスをつけるだけで指示は同様です。-0-3、-2-3、-3.
 後ろの行をコピーする場合も、コピー文はカーソルのある行の前にコピーします。
 では、0と指定した場合はどういう意味になるでしょうか。カーソルの行をカーソルの行の前にコピーすることにします。-3-(+2)というような意味合いの指定は出来ないこととします。
 禁止事項はこれ以外にも、3-2、-3-2もダメとします。
 (QXマクロの一般的なお約束事は第33回及び34回参照)

 流れのポイントを見て行きます
 1.カーソルのある行の行番号を取得する
 2.コピーする箇所の入力。2-3など。
 3.入力値の分解
 4.コピーすべき行を配列変数に入れる(MAX100行としました)
 5.カーソルを、最初の行の前に置く
 6.コピーした文を貼付ける、改行を貼付ける。これを指定した回数繰りかえす。
 7.貼付けた一群の行の先頭行の行頭にカーソルを置く

 1.カーソルのある行の行数を取得する
  y_begin = @ScrLineToCrLine(@Line) '物理行から論理行への変換
  ※@Lineそのものは物理的な行数(表示単位の行数)となっています。
 2.コピーする箇所の入力
msg$ = "コピーしたい範囲の行数を入力してください。後ろの文章の場合はマイナス付き。例;3-5、-2-4、3、-3"
in1$ = inputbox$(msg$,,"0-2")
if in1$ = "99" then exit proc
 99が入ってきた場合は終了としました。
 3.入力値の分解。開始行、終了行の把握
 入力値の中の"-"の位置を調べます。
 ない場合…前の単一行の指定=⇒そのものが開始行であり終了行
 ある場合でその場所が1ではない…前の複数行の指定=⇒"-"の前が終了行、後が開始行
 ある場合でその場所が1でありほかに"-"がない…後ろの単一行の指定=⇒そのものが開始行であり終了行
 ある場合でその場所が1でありほかに"-"がある…後ろの複数行の指定=⇒"-"の前が開始行、後が終了行
 "-"の場所は、 p1=instr(in1$,"-")でもとめます。
 二つ目の"-"の場所は、 p2=instr(mid$(in1$,p1+1),"-")でもとめます。
 ここで使う変数は文字型なので、分解したあとvalで数字型にしておきます。
 前の複数行指定の場合で、一つ目の"-"の前と後ろは、
in19 = val(left$(in1$,p1-1))*(-1)
in11 = val(mid$(in1$,p1+1))*(-1)
で取り出します。開始・終了行が逆のように見えるのに注意してください
 後の複数行指定の場合では、一つ目の"-"が一つ余計についているので、
in11 = val(mid$(in1$,p1+1,p2-1))
in19 = val(mid$(in1$,p1+p2+1))
で取り出します。

 4.該当する行を配列変数に入れる(MAX100行としました)
 最初にカーソルがあった行からの相対的な位置は、in11とin19に入っています。
 この場合は、前はマイナス、後はプラスの数値となっています。
 コピーすべき行の相対的位置は、in11からin19になります。
 それを配列変数に格納しておきます。
'コピーもとの行に移動
@Line = @CrLineToScrLine(y_begin+in11)
 ( )の中は論理行となっています。それを表示上の行にしています。
gyo_cnt = in19-in11+1
'格納処理。
for i = 1 to gyo_cnt
mm$[i] = @TextCr$(@Line) 論理的な一行分の取得はこうします。改行は含まれません
@MoveNextLineCr '次の行(論理行)へ
next

 5.カーソルを、最初の行の行頭に置く
 ここまでは、行を追加していませんので行の関係は最初のままです。
 最初の行に制御を移します。
@Line = @CrLineToScrLine(y_begin)

 6.コピーした文を貼付ける、改行を貼付ける。これを指定した回数繰りかえす。
@MoveBeginningLine
for i = 1 to gyo_cnt
@Insert mm$[i]
@InsertF "\n"
next
これは説明が不要だと思います。

 7.カーソルを貼付けた一群の行の先頭行の行頭に置く
 前の場合、後の場合とも、最初の行となります。。
 したがって、
  @Line = @CrLineToScrLine(y_begin)
 となります。

 結構かかりました。今回はここまでとします。

2008年4月20日日曜日

第90回目 QXマクロの紹介

○第90回目 QXマクロの紹介
 ※QXマクロは、QXエディタ(テキストエディタ)で使用されるマクロのことです。
  QXマクロでは、全角スペースはデーター以外は認められていません。コピーして使う場合は、必ず半角スペースに変えてください。

 文章作成の効率を若干上げるマクロです。
 マクロを含めて文章を作成する際に一番よく使うのは何でしょうか。当然文字の入力かかる入力は除きます。
 人によって異なるものだと思いますが、コピー、移動、削除が多いのではないでしょうか。
 ということでこれらを中心にマクロの紹介です。
 ニーズ
【コピー】
 ・この行の前の部分をコピーしたい
 ・この行全体をコピーしたい
 ・前の行のあの部分をコピーしたい
 ・後ろの行のあの部分をコピーしたい
 ・あの行をコピーしたい(それをもとに修正すれば早い)
【挿入】
 ・この部分をカッコでくくりたい。いろいろなカッコを選びたい
 ・複数の行に連番を振りたい
 ・複数の行のある位置に同じ文字列を挿入したい
【特殊コピー】
 ・この文書の全文を別の文書のある部分にコピーしたい
 ・文末までをクリップボードにコピーしたい
 ・ブロックコピーをしたい
【逆転】
 ・あの行をコピーして、中を逆転させればいいのではないか(特にマクロでは、"="の前後を変えたいということがよく起こります)
 ・前後の行を逆転させたい
【削除】
 ・一時一字削除していくのではなく、何文字か一気に削除できないか
 ・文末まで削除したい
 ・一行を簡単に削除したい
 ・何行かをいっぺんに削除したい

 例えば、上のような"まとめの文章"があったとします。これ("・"の行のみ)に連番を振りたいのですが、どうしますか。連番は、"1."、"2."、"3."というものとします。
 今把握している連番マクロでは、指定されたすべての行となっていますので、【 】の行にも連番が振られてしまい、一気には出来ません。何段階に分けて実行するしかありません(【 】を除いて作った段階で、連番を振るというのであれば可能です)。
 この例のように文書を作成する場合においても、結構マクロが便利なのです。

 連番マクロで番号を振ってみました。
 既存のマクロ等の場合はコメントを付しています。
【コピー】
 1.この行の前の部分をコピーしたい
 2.この行全体をコピーしたい=⇒QXエディタの基本機能
 3.前の行のあの部分をコピーしたい
 4.後ろの行のあの部分をコピーしたい
 5.あの行(複数行も可能とする)をコピーしたい(それをもとに修正すれば早い)
【挿入】
 6.この部分をカッコでくくりたい。いろいろなカッコを選びたい=⇒括弧入力マクロ
 7.複数の行に連番を振りたい=⇒連番自動入力マクロ
 8.複数の行のある位置に同じ文字列を挿入したい
【特殊コピー】
 9.この文書の全文を別の文書のある部分にコピーしたい
 10.この文書のこの部分を別の文書のある部分にコピーしたい
 11.文末までをクリップボードにコピーしたい
 12.ブロックコピーをしたい=⇒「箱形でペタペタ」マクロ
【逆転】
 13.あの行をコピーして、中を逆転させればいいのではないか(特にマクロでは、"="の前後を変えたいということがよく起こります)
 14.前後の行を逆転させたい。一行の移動も一種の逆転でしょうか。含めましょう。
【削除】
 15.一字一字削除していくのではなく、何文字か一気に削除できないか
 16.文末まで削除したい=⇒QXエディタの基本機能
 17.一行を簡単に削除したい=⇒QXエディタの基本機能
 18.何行かを一気に削除したい

※ここに掲げた"括弧入力マクロ"等の記載のある部分は、すでにフリーのマクロとしてあるものです。

 ここに掲げた機能の全部が必要なものとはいえないと思います。筆者も、ほとんど使わないのは、11と15と18です。
 18なんかは、削除したい行数を数えるという頭の動きに慣れていないと、めんどくさくなり、既存の操作になる傾向があります。使いこなすには、視覚的に全体をみて瞬時に判断するとかの頭の訓練する必要があります。

2008年4月12日土曜日

第89回目 ブック(ファイル)を超えての処理の仕方(本題)

○第89回目 ブック(ファイル)を超えての処理の仕方(本題)
 ※エクセル2007で行なったものです

 今回はファイルを越えてです。
 ファイルの中に入れば、前回の処理で全シートの中身を置き換えていくという処理になります。
 ですから、前回の処理(置換語等の情報を取得したあと)に初めとおわりを付け加えればよいことになります。
 流れを頭の中に入れておきましょう。
 1.置換語等の取得
 2.パス名等の取得
 3.ファイルを閉じる
 4.フォルダ内のエクセルファイルを開く
 5.全シートに対して置換え処理をする
 6.当該ファイルを閉じる
 7.次のファイルを開き、処理を繰り返す。ファイルがない場合は終了。

 2.パス名等の取得
myPath = ActiveWorkbook.Path
 =⇒パス名は、フォルダの名前までが取得されます。
ChDir myPath
=⇒フォルダを指定します。
 ファイルを開く時には、フルパス名で開くことにします。

 3.ファイルを閉じる
ActiveWorkbook.Close SaveChanges:=True
 最初のファイルは閉じておきます、これをしないと二重に開くということが起きるので、その回避が面倒くさいので、閉じることにします。

 4.フォルダ内のエクセルファイルを開く
  フォルダに入っているエクセルのファイル名が必要です
   myFName = Dir(myPath & "\*.xls")
  =⇒これで取得します。myFName にはファイル名が入ります。パス名ではありません。
  (myFName = Dir("*.xls") でいいはずなのですが、フォルダの指定が出来ていないため、デフォルト値のフォルダをさがしにいってしまう)
  まず一つ取得しておいて、あとはdo loopで繰り返します。

Do Until myFName = ""
=⇒フォルダに次のファイルがなくなると、ファイル名はヌルが取得される
myFName = myPath & "\" & myFName
=⇒'フォルダ間でのパス名とファイル名をつけて完全パス名にしています。
Workbooks.Open Filename:=myFName
  =⇒ファイルを開く命令
sh_no = ActiveSheet.Index
=⇒処理が終わったときに開いた時点のシート再現するために取得する。

 5.全シートに対して置換え処理をする=⇒前回参照

 6.当該ファイルを閉じる
Worksheets(sh_no).Select
=⇒開いた時のシートに戻す
ActiveWorkbook.Close SaveChanges:=True
=⇒ファイルを保存する。確認のメッセージは出ません。

 7.次のファイル名を取得し、処理を繰り返す。ファイルがない場合は終了。
myFName = Dir()
  =⇒次のファイル名を取得
     myFName = Dir("")とすると最初からになるので、ぐるぐるまわる。
Loop

 これで終了ですが、終了する前に最初に開いてたファイルを開いて最初の状態を再現するには、最初のファイルのパス名とシート番号を別にもっておいて開きなおしてください。
 完全パス名 ThisWorkbook.FullName

※今回は置換えだったので、シート内の表の形式はまったく関係ありませんでした。
 しかし、ファイル内のシートがまったく同じ形式、かつ、フォルダ内のすべてのファイルも同じということもあります。すると、今回の置換えではないロジックを含んだ複雑な処理が出来ます。そのようなケースで大量処理の場合、夜始動させておいて、朝に出来上がっているという、芸当ができ、時間の節約が可能となります。
 また、マクロ中にフォルダのパス名を記述する方法もあります。すると複数のフォルダの処理が可能となります。
 応用できる事例があると腕のふるいがいがありますね。いちやく一目置かれるかも(保証なし)。

2008年4月6日日曜日

第88回目 ブックを超えての処理の仕方(背景など)

第88回目 ブックを超えての処理の仕方(背景など)

 必要は成功の母です。
 エクセルのファイルの入っているフォルダをコピーします。
 すると、一つのエクセル
のファイルから見て、リンク関係は、
 1.同じフォルダにあるエクセルのファイルからのリンクは正しくコピーされます。
 2.違うフォルダに入っているエクセルファイルからのリンクは正しく引き継がれません。ここで正しく引き継がれないとは、前の一群のフォルダにリンクが張られたままだということです。違う見方でいうと、リンク元のフォルダまでコピーしたのにフォルダ外からのリンクは、もとのフォルダのままだということです。これをフォルダガイストというといった笑われましたが…。
 これを直すには、リンク先を新しくコピーしたフォルダのパスに置き換える必要がです。
 【概 略 図】










 簡単です。一つ一つのエクセルファイルを開いて、一つのシートごとに置換えをしていき続ければいいのです。
 簡単ですが、面倒です。
 今まで紹介した機能では、複数語の置換えというのがありました(第20回)。
 これは複数の文字列の置換えを作成しておき、そのセルを指定し、さらに置き換え範囲を指定して(同一シート)、置換えを行うものです。
 この機能を拡大し、
 1.ファイル内のすべてのシートに対して置換えを行う。
 2.フォルダ内のすべてのエクセルファイルのすべてのシートに対して置換えを行う。
 とするものです。
 手順としても機能としても、原型を含めて三つのものを作成しておくのがいいでしょう。
 すなわち、
 1.シート内の指定した範囲内に対して置換えを行う。
 2.ファイル内のすべてのシートに対して置換えを行う。
 3.フォルダ内のすべてのエクセルファイルのすべてのシートに対して置換えを行う。
 という機能となります。

 2は簡単です。1の機能をシートごとに処理することにすればいいのです。for to文でシートの数だけ繰り返せばいいのです。
 問題(気をつけるべき点は)は、二つあります。
 1.ワークシートだけにするのかグラフのシートを対象にするのか
 =⇒グラフシートでは置換えは効かないと思われるのでワークシートだけに限定する
 2.処理が終了したとき、複数置換え語が書かれているシート(実行時のシートのこと)に処理を移すにはどうするか
 基本事項を次に書いておきます。
 ワークシートだけを処理する
  For j = 1 To Worksheets.Count 'グラフを入れる場合は To Sheets.Count
 Worksheets(j).Select
ワークシートの全部のセルを指定
  Cells.Select
 シート番号を取得しそのシートを指定する
 ii0 = Sheets.Index
  Sheets(ii0).Select

 後の機能は第20回を参照してください。
 フォルダ内のエクセルファイルへの対応は次回にします。



2008年3月29日土曜日

第87回目 図形作成システムの全体像

○第87回目 図形作成システムの全体像

 これまでに作られた、図形作成システムの全体像を見てみます。
 当初考えていたものから、もっともっととなって、2倍から3倍近く膨らんでしまいました。やはり実際に使ってみて資料を作ってみると、もっと機能がほしいということになってしまいました。
 共通マクロを除くと、31個の機能となっています。機能的にはだぶっているのもあります。例えば、"その他の属性情報により図形を修正"は、【図形の属性の修正】とほとんどダブっているといえます。図形作成管理表に書き込むことにより、個別に操作しなくても図形を修正することができます。ケースバイケースで使用してください。個人的には、図形を見ながら個別の属性を修正するというのが、性にあっているようです。

 これで、図形作成にかかるマクロに関する説明は終了することにします。

【図形を作成・削除】
 図形の複数一括作成
 図形をコネクタで結ぶ(コネクタの作成)
 図形_画像の埋込み
 図形の削除
 コネクタの削除

【図形の位置の調整】
 図形の移動_場所を指定する
 図形の整列(複数の図形をある基準で整列させる)
 一つの図形の移動
 図形の移動_整列もどき(規則性を持たせて並べる)

【図形の属性の修正】
 図形の種類の変更
 図形のテキストの修正
 文字の変更
 文字の配置
 図形サイズの変更
 図形_色の選択
 図形の外枠の設定_消去
 図形の回転
 図形ライン_コネクタの修飾
  ラインの太さ
  矢印の向き
  点線化

【図形の属性の高度な修正】
 図形のサイズ等を合わせる
 その他の属性情報により図形を修正

【図形情報の取得】
 図形のテキストの取得_書き込み
 図形の位置情報の取得
 コネクタの場所の取得
 図形のその他の属性の取得
 図形情報の一括取得

【その他】
 図形のテキストに図形の番号を追加_削除
 図形のグループ化及び解除
 図形の順序
 図形番号の並べ替え

【共通マクロ】
 指定範囲の取得
 図形番号の指定

2008年3月22日土曜日

第86回目 図形作成システムの更なる完成度を求めて(その3)

○第86回目 図形作成システムの更なる完成度を求めて(その3)

 今回は
 4.図形のその他の属性の取得
 5.図形のその他の属性により図形を修正
 6.図形情報の一括取得(コネクタ情報は除く)
 です。

 ある時、"図形のサイズ等を合わせる"処理をしていました。
 ほとんど気にせずにパラメータを指定していたのですが、何かの関係で、全部の図形をという指定になっていたようです。それで実行させたものですから、すべての図形が皆同じになってしまいます。せっかく、作り上げたのに、位置関係、コネクタは大丈夫ですが、大体の部分の作成処理は無駄になってしまいました。おそらく、IMEのオンオフ状態が関係していたのでしょう。
 そんな失敗から、4,5番の機能を考えました。
 これは、第84回の"図形のサイズ等を合わせる"をもとにするものです。
 すべての図形のその他の情報を取得してセルに書き込むというものです。この機能と、5の逆の機能を組み合わせれば、原状の復帰が可能となるのです。
 その他の情報としては、次のものです。

AA;図形の種類
AB;サイズ_幅
AC;サイズ_高さ
AD;ラインの太さ  ライン及びコネクタのみ
AE;実線・点線か  ライン及びコネクタのみ
AF;矢印_先頭    ライン及びコネクタのみ
AG;矢印_最後    ライン及びコネクタのみ
AH;内部の色    画像、ライン及びコネクタは除く
AI;透明度     画像、ライン及びコネクタは除く
AJ;フォント_種類  画像、ライン及びコネクタは除く
AK;フォント_サイズ 画像、ライン及びコネクタは除く
AL;文字配置_縦   画像、ライン及びコネクタは除く
AM;文字配置_横   画像、ライン及びコネクタは除く
AN;外枠有無     画像、ライン及びコネクタは除く
AO;回転       コネクタは除く
AP;パスネーム   対象外

 最後のパスネームは画像を張り付けた時の元の画像のあり場所です。しかしながら、この情報の取得はうまくいかないので、現在では対象外としています。
 (画像はエクセル内の取り込まれてしまうので、もとの場所が不要となることから、取得ができないのではないかと思います。この情報は別のところに、例えば欄外に保管すべきものなのでしょう)
 これらの情報は、図形のサイズ等と同じく、設定と取得が逆の関係になっています。
 また、取得は内部コードとなりますので、そのままセルに書き込みます。設定の時はその内部コードを記述すれば、図形に反映されます。
 注意すべきは、すべての図形にすべての情報があるとか限らないことです。情報が取得できなくて、エラーになってしまう場合があります。
 また、図形の種類はよくわからないので、図形の名前から判断しています。2003エクセルでは図形の名前が統一化されているものがありますので、取得はできません。
 更に、グループ化されている図形は一つの図形となっていまいますので、その情報からは復元できません。グループ化を解いて情報の取得をしてください。グループ化した図形に対して復元をかけるとエラーになります。回避する手段をしてください。何回か試行錯誤すればわかります。

 図形の種類
 これは困りました。つまり、図形の名前で、図形の種類がわかるものなのか、ということです。
 エクセル2003の場合は、多くの種類の図形名前がAutoshapeとなっていますので、これからは図形の種類の取得は出来ません。(他の手段がわからないので、現時点ではそう思っていますというという意味です)
 2007を優先にし、図形の名前の中に該当する文字列があるかで決めます。

 ※mm2(i)は図形の名前
temp1 = mm2(i)
If InStr(temp1, "角丸四角形") <> 0 Then z_syu = 1
If InStr(temp1, "円") <> 0 Then z_syu = 2
If InStr(temp1, "爆発") <> 0 Then z_syu = 3
If InStr(temp1, "円形吹き出し") <> 0 Then z_syu = 4
If InStr(temp1, "直線コネクタ") <> 0 Then z_syu = 5
If InStr(temp1, "Line") <> 0 Then z_syu = 5
If InStr(temp1, "右矢印") <> 0 Then z_syu = 7
If InStr(temp1, "正方形/長方形") <> 0 Then z_syu = 8
If InStr(temp1, "Rectangle") <> 0 Then z_syu = 8
If InStr(temp1, "フローチャート") <> 0 Then z_syu = 9
If InStr(temp1, "ホームベース") <> 0 Then z_syu = 10
If InStr(temp1, "山形") <> 0 Then z_syu = 11
If InStr(temp1, "右中カッコ") <> 0 Then z_syu = 121
If InStr(temp1, "左中カッコ") <> 0 Then z_syu = 131
If InStr(temp1, "右大カッコ") <> 0 Then z_syu = 122
If InStr(temp1, "左大カッコ") <> 0 Then z_syu = 132
If InStr(temp1, "中カッコ") <> 0 Then z_syu = 141
If InStr(temp1, "大カッコ") <> 0 Then z_syu = 142
If InStr(temp1, "カギ線コネクタ") <> 0 Then z_syu = 31
If InStr(temp1, "図") <> 0 Then z_syu = 32
If InStr(temp1, "Picture") <> 0 Then z_syu = 32
If InStr(temp1, "Group") <> 0 Then z_syu = 90
 ※図形の種類(番号)が12以上のものは、それだけでは図形が再現できない、または、既存の作成システムでは二段階に分けているので、アウトプットとしての図形の種類を系列化してする、という意味です。(大カッコと中カッコは後者の例です。前者の例はグループ化された図形です)

・サイズ_幅
・サイズ_高さ
・ラインの太さ  ライン及びコネクタのみ
 これは、設定と同じ内容ですので説明不要です。

・実線・点線か  ライン及びコネクタのみ
 実線の場合は、1となります(内部コード。以下同じ)
 点線の場合は、3となります

・矢印_先頭    ライン及びコネクタのみ
・矢印_最後    ライン及びコネクタのみ
 最初も最後も矢印有は2、無は1

・内部の色    画像、ライン及びコネクタは除く
 カラーコードは、ForeColor.SchemeColorでのものを取得します。

・透明度     画像、ライン及びコネクタは除く
・フォント_種類  画像、ライン及びコネクタは除く
・フォント_サイズ 画像、ライン及びコネクタは除く
 これは、設定と同じ内容ですので説明不要です。

・文字配置_縦   画像、ライン及びコネクタは除く
・文字配置_横   画像、ライン及びコネクタは除く
縦の上中下 -4160、-4108、-4107
横の左中右 -4131、-4108、-4152
 となっています。

・外枠有無     画像、ライン及びコネクタは除く
外枠有の時 -1、無の時 0
 となっています。

・回転       コネクタは除く
 角度(一周360°)が取得されます。
 2003では直線の回転はありません。
 反映はエラーになります。

・パスネーム   対象外
 図形は、エクセル内部に含まれてしまうので、元のパス名の取得は無理なようです。
 別に管理する必要があります。

 これらの情報の図形への反映は、逆のことをすればいい(これまで図形の属性として設定したことをやればいいのです)ので、それほど複雑ではないと思います。

 6.図形情報の一括取得(コネクタ情報は除く)
 これは、今までのマクロの積み上げです。サブルーチン化されているものを、次々に呼び出して処理を行うというものです。
 上のその他の情報以外にものとしては、テキストの情報、位置の情報、コネクタの情報があります。このうちコネクタに関する情報を除きました。
 コネクタの情報は、こちら側から与えるもののなので、最終的な確認など他の情報とは更新のタイミングが異なるのではないかと思っています。それゆえに一括取得からは除きました。
 この機能は、今での逆となりますが、一つだけ、"図形の種類の変更"には注意が必要です。
 しかしながら、この機能は、"図形の種類の変更"に含まれていますので、これをコピーするか、サブルーチン化して呼び出すかという。いずれかの方法となると思います。モジュールの中で同じ機能が二つ存在することになりますが、"コピーする"のほうが単純なので簡単ですね。
 "サブルーチン化して呼び出す"はできてからでもいいのではないでしょうか。まずは成果物を出すことのほうが優先ではないでしょうか。

2008年3月16日日曜日

第85回目 図形作成システムの更なる完成度を求めて(その2)

○第85回目 図形作成システムの更なる完成度を求めて(その2)

 続きです。
 3.図形番号の設定の改善
 この機能の目的は、飛び飛びの図形番号を指定できるようにするということです。
 今までは、全部の図形、一つの図形、ある番号からある番号までの図形のいずれかという指定の仕方しか出来ませんでした。
 一つの図形及びある番号からある番号までの図形を複数個指定できるようするものです。例えば、3.5-9.11.16.20-28とか指定します。
 この意味は、解説するまでもなく、3.5.6.7.8.9.11.16.20.21.22.23.24.25.26.27.28の図形を指定しているものです。
 複雑かなと思っていましたが。、意外とすっきりしていました。
 今までの機能では、
 3が入れば3と3
 5-9が入れば、5と9
 というのが出力されます。
 この機能をそのままにして、1.出てきた結果を格納し、2.必要なだけ繰り返す、という二つの機能を追加すればいいのでした。
 番号を格納する変数を、zz_no(200)としました。これはモジュール変数です。モジュールの最初に記述してください。
 この変数の中には、上の例では、3.5.6.7.8.9.11.16.20.21.22.23.24.25.26.27.28とうものが、順番に格納されます。zz_no(1)=3、zz_no(2)=5とかいうようにです。
 既存の機能での出力は、no1とno9となります。ですから
 for i= no1 to no9
  zz_no(i-no1+1) = i
 next i
 となります。
 これまの処理で何個(ii5)か格納されているので、正確には
  zz_no(ii5+i-no1+1) = i
 となります。

 頭に戻りまして、入力値を3とか5-9とかに切り分ける処理です。
 これも"."の位置を取得すればいいことになります。
 その位置の前の部分が、既存の処理の入力値となります。
 そして、次の回の処理には、その部分を除いたもので処理を続ければいいことになります。
 最初はいいのですが最後はどうなるでしょうか。実質的な最後は、"."がなくてヌルでないということになります。次の回の処理は、使った部分は捨てられますので、文字列はヌルになります。ここでループを出ることになります。
 入力値 3.5-9.11.16.20-28
 最初の切り出し 3。残りは、5-9.11.16.20-28
 次の切り出し 5-9。残りは、11.16.20-28
 次の切り出し 11。残りは、16.20-28
 次の切り出し 16。残りは、20-28
 次の切り出し 20-28。(この段階で"."はない)残りは、ヌル
 次の切り出し ヌルなのでループを抜ける。

 格納した図形の数(ii5)は、初期値が0で、各回の処理ごとの図形の数を加算していきます。各回の図形の数は、"no9-no1+1"となります。

 最初の部分は次のようになります。
 Do
'終了は、文字がない時
If z_no15 = "" Then Exit Do

p2 = InStr(z_no15, ".")
If p2 = 0 Then
z_no = z_no15 '最後の場合
z_no15 = ""
Else
z_no = Left(z_no15, p2 - 1)
z_no15 = Mid(z_no15, p2 + 1)
End If
z_no は既存の処理で使われる変数。
 【既存の処理】

 後の処理は、図形番号の格納ですが、つぎのようになります。
 For i = no1 To no9
  zz_no(ii5 + (i - no1 + 1)) = i
 Next i
ii5 = ii5 + (no9 - no1 + 1)
Loop
 最後に
zz_no_max = ii5 として最終的な図形の数を格納しておきます。
 ※zz_no_maxはモジュール変数です。

 すでに、図形番号の指定はサブルーチン化していますので、一つ修正すればよいことになります。しかし、今回は出力値が変わってきますので、サブルーチンの読み込んだマクロ側の修正が必要とあります。
 今までの出力値は、no1とno9でしたが、これからはzz_no(i)と zz_no_max です。
 ですから処理側では、今までは
 for i = no1 to no9
  ActiveSheet.Shapes(i).Select
  処理
 next i
 となっていましたが、今後は、
  for i=1 to zz_no_max
   j= zz_no(i)
  ActiveSheet.Shapes(j).Select
    処理
  next i
 となります。
 図形の削除など、逆から進むものもありますので注意願います。
 この場合は、一行目のステップが
  for i= zz_no_max to 1 step -1 となります。
 ※"no1 to no9"、"no9 to no1"、"no1"、"no9"などの文字列で、モジュール内の該当箇所を探してください。意外と修正漏れがあるものです。