いらっしゃいませ、277610番目のお客様。  

[Excel]実用(?)マクロ集

このページに含まれるプログラム、ファイル等については、

  1. 一応、著作権は放棄してません。転載等される方は私にご連絡ください。
  2. 内容については万全を期していますが、利用については自己責任でお願いします。いかなる損害にも責任を負いません。
  3. 動作確認は、(1)Windows 98+Excel 2000; (2)Windows XP+Excel 2002で行っています。
以上の点にご注意願います。


目次

  1. 選択範囲を図としてコピー
  2. 複数の条件に合う行の指定された列の値を返す
  3. ある列の値が指定の文字列である行をすべて選択
  4. 色付きのセルだけ集計
  5. 指定された行をそれぞれn倍にする
  6. 特殊なセルの選択をするダイアログを開く
  7. 選択した範囲の値を括弧で囲む
  8. 表の行数を増やす
  9. テキストボックスを作る
  10. 表から重複行の削除
  11. ファイルのバックアップを作成する
  12. 保護されたセルへの入力
  13. ロックされていないセルのみに移動
  14. セルの幅、高さの調整
  15. セルに合わせた図形・グラフ等の拡大・縮小
  16. 座標の変換
  17. 数字を抜き取る
  18. フロッピーのデータをハードディスク上で扱う
  19. 数式を値に置換する
  20. シートをCSV出力

Office VBAの部屋に戻る

選択範囲を図としてコピー

プログラム

Sub CopyAsPicture()
    Dim tempBoolean As Boolean
    tempBoolean = ActiveWindow.DisplayGridlines
    ActiveWindow.DisplayGridlines = False
    Selection.CopyPicture Appearance:=xlScreen, Format:=xlPicture
    ActiveWindow.DisplayGridlines = tempBoolean
End Sub

範囲を選択して実行すると、その範囲内をエクセルで自動に付ける薄い枠線を削除してから図としてコピーします。


目次に戻る

複数の条件に合う行の指定された列の値を返す

プログラム

Function Vlookup2(SA As Range, col As Integer, ParamArray cond() As Variant) As Variant
    Dim a As Range, r As Range
    Dim count As Integer, c As Variant, check As Boolean
    For Each a In SA.Areas
        For Each r In a.Rows
            count = 1: check = True
            For Each c In cond()
                If r.Columns(count) Like c Then
                    count = count + 1
                Else
                    check = False: Exit For
                End If
            Next c
            If check Then Vlookup2 = r.Columns(col): Exit Function
        Next r
    Next a
    Vlookup2 = 0
End Function

プログラム例

    =VlookUp2(B2:E5,4,"7","Wed")

範囲内で最初に1列目(B列)に7、2列目(C列)にWedが入っている行の4列目(E列)の値を返す。


目次に戻る

ある列の値が指定の文字列である行をすべて選択

プログラム

Function VlookArea(str As String, SA As Range, Optional col As Integer = 1) As Range
    Dim temp As Range, a As Range, r As Range
    Set temp = Nothing
    For Each a In SA.Areas
        For Each r In a.Rows
            If str Like r.Columns(col).Value Then
                If temp Is Nothing Then
                    Set temp = r
                Else
                    Set temp = Union(temp, r)
                End If
            End If
        Next r
    Next a
    Set VlookArea = temp
End Function

プログラム例(VBA関数内)

    VlookArea("test",Range("B2:E5"),2).select

2列目(C列)にtestとかかれている行をすべて選択する。


目次に戻る

色付きのセルだけ集計

プログラム

Function SumColor(rngSelect As Range, intColor As Integer) As Double
    Dim a As Range, r As Range, sum As Double
    sum = 0#
    For Each a In rngSelect.Areas
        For Each r In a.Cells
            If r.Interior.ColorIndex = intColor And IsNumeric(r) = True Then sum = sum + r
        Next r
    Next a
    SumColor = sum
End Function

プログラム例

    =SUMCOLOR(A1:A5,3)

選択した範囲の中でColorIndexが3(赤)で塗りつぶされたセルを合計する。


目次に戻る

指定された行をそれぞれn倍にする(2001/08/10)

プログラム

Function RowDoubling(Optional DoubleNum As Integer = 0, Optional Target As Range = Nothing) As Range
    On Error GoTo ErrorRowDoubling
    Dim i As Integer, j As Integer, RowNum As Integer, r As Range
    If Target Is Nothing Then Set Target = Selection
    If DoubleNum = 0 Then DoubleNum = CInt(InputBox("何倍にしますか?", , "2"))
    RowNum = Target.Rows.count
    If RowNum * DoubleNum > Target.Parent.Rows.count Or DoubleNum < 2 Then
        Set RowDoubling = Target
    Else
        Application.ScreenUpdating = False
        Set r = Target.Rows(1)
        For i = 1 To RowNum
            For j = 2 To DoubleNum
                r.Copy
                r.Insert Shift:=xlShiftDown
            Next j
            Set r = r.Offset(1, 0)
        Next i
        Set RowDoubling = Range(Target.Rows(1).Offset(-DoubleNum + 1, 0), r.Offset(-1, 0))
        Application.CutCopyMode = False
        Application.ScreenUpdating = True
    End If
    Exit Function
ErrorRowDoubling:
    Set RowDoubling = Target
    On Error GoTo 0
End Function

プログラム例(VBA関数内)

    RowDoubling(3).select

同じxの値に複数のyの値があるような表を書くときにxの値の入力に便利です。自作関数は表示されませんが、マクロとして実行することも可能です。


目次に戻る

特殊なセルの選択をするダイアログを開く(2001/8/10)

プログラム

Sub DispSpecialCell()
    On Error Resume Next
    Application.Dialogs(xlDialogSelectSpecial).Show
    On Error GoTo 0
End Sub

セル範囲を選択した状態で実行してください。選択されたセルの中から色々な条件に合うセルを選択することができます。


目次に戻る

選択した範囲の値を括弧で囲む(2001/8/17)

プログラム

Sub Kakko(Optional Target As Range = Nothing, Optional OP As String = "(", Optional CL As String = ")")
    On Error GoTo KakkoError
    Dim a As Range, r As Range, StdFont As Font
    If Target Is Nothing Then Set Target = Selection
    For Each a In Target.Areas
    For Each r In a.Cells
        With r.Characters(1, 1)
            .Insert OP & .Text
        End With
        With r.Characters(r.Characters.count, 1)
            .Insert .Text & CL
        End With
    Next r
    Next a
    Exit Sub
KakkoError:
    r = "'" & r
    Resume
End Sub

範囲を選択した状態でマクロの実行をすると、数値/文字列にかかわりなく、値を括弧で囲みます。文字ごとに設定された書式を保持します。ただし括弧も、1番はじめの文字(あるいは最後の文字)の書式に影響されます。また、別の関数から引数付きで呼び出すことにより括弧の形状、変換する範囲が指定できます。

プログラム例(VBA関数内)

Sub 大括弧()
    Kakko Selection, "[", "]"
End Sub


目次に戻る

表の行数を増やす(2001/8/24)

プログラム

Sub IncLine()
    Selection.Copy
    Selection.Insert Shift:=xlDown
    Application.CutCopyMode = False
    Selection.Offset(1, 0).Select
    Selection.ClearContents
End Sub

「行を増やす前にデータを入力してしまった」という時に、同じ形式の行を追加するのに便利です。最終行を選択して、マクロで実行してください。表に定義してある、書式、名前等も拡張されます。


目次に戻る

テキストボックスを作る(2001/10/12)

プログラム

Sub MakeTextBox(Optional dummy As Integer = 0)
    Dim ttop As Single, tleft As Single, theight As Single, twidth As Single
    Dim nowShape As Shape
    Dim nowSelection
    If TypeName(Selection) = "ChartArea" Then
        Set nowSelection = Selection.Parent.Parent.ShapeRange
    Else
        Set nowSelection = Selection
    End If
    With nowSelection
        ttop = .Top
        tleft = .Left
        theight = .Height
        twidth = .Width
    End With
    Set nowShape = ActiveSheet.Shapes.AddShape(msoShapeRectangle, tleft, ttop, twidth, theight)
    With nowShape
        .Fill.Visible = msoFalse
        .Line.Visible = msoFalse
        .Select
        With .TextFrame
            .MarginLeft = 0#
            .MarginRight = 0#
            .MarginTop = 0#
            .MarginBottom = 0#
            .Characters.Text = ""
        End With
    End With
End Sub

現在選択されているものの大きさに合わせて移動可能なテキストを書くための図形を作成します。枠無し、背景無しにしますので、見失わないように注意してください。でも見失った時のためのプチプログラムを用意しました。

プログラム2

Sub ShapesSelectAll(Optional dummy As Integer = 0)
    ActiveSheet.Shapes.SelectAll
End Sub

小粒でピリリと効くプログラムでしょう。


目次に戻る

表から重複行の削除(2001/11/21)

SOFT-Xさんのホームページを賑わせていた私のプログラムを紹介します。表の内のある項目に注目して、重複している行があれば削除するというものです。このプログラムの特徴は

  1. 検査項目の並びがばらばらでも機能する。
  2. 重複行のうち一番最初の行を残すか、一番最後の行を残すか選択できる。
  3. 表のタイトルや脚注がつくような表にも対応できる。
  4. マクロとしても実行できる。その場合、カーソルのある列で表を検索します。
というところです。

プログラム

'Table:チェックする表を指定します。(データ部分のみを指定しますが、通常、タイトル行はデータと重複することはないと思いますので、含まれてもかまいません。)
'ColumnNo:検査する項目の番号(表内での列の番号です。)
'Order:重複行のうちxlAscendingは一番最初の行を残し、xlDesendingは一番最後の行を残します。
Function TrimTable(Optional ByVal Table As Range = Nothing, Optional ByVal ColumnNo As Long = 1, Optional Order As XlSortOrder = xlAscending) As Range
    Dim WorkRange As Range
    Dim Start As Long, Max As Long, l As Long
    Dim NowString As String
    If Table Is Nothing Then
        Set Table = Selection.CurrentRegion
        ColumnNo = ActiveCell.Column - Table.Column + 2
    Else
        ColumnNo = ColumnNo + 1
    End If
    Max = Table.Rows.Count
    If Max > 1 Then
        Application.ScreenUpdating = False
        With Table
            .Cells(1).EntireColumn.Insert xlShiftToRight
            Set WorkRange = .Offset(0, -1).Resize(ColumnSize:=.Columns.Count + 1)
        End With
        With WorkRange
            .Cells(1) = 1
            .Cells(1).AutoFill .Columns(1), xlFillSeries
            .Sort Key1:=.Cells(1, ColumnNo), Order1:=xlAscending, Key2:=.Cells(1), Order2:=Order, Header:=xlNo, OrderCustom:=1, MatchCase:=True, Orientation:=xlSortColumns
'TrimPartStart
            NowString = .Cells(1, ColumnNo).Text
            Start = 2: l = 2
            Do
                If .Cells(l, ColumnNo).Text <> NowString Then
                    If Start <> l Then
                        Range(.Rows(l), .Rows(Max)).Copy .Cells(Start, 1)
                        Range(.Rows(Max - l + Start + 1), .Rows(Max)).ClearContents
                        Max = Max - l + Start
                        l = Start
                    End If
                    Start = l + 1
                    NowString = .Cells(l, ColumnNo).Text
                End If
                l = l + 1
            Loop While l <= Max
            If Start <> Max + 1 Then
                Range(.Rows(Start), .Rows(Max)).ClearContents
                Max = Max - Start + 1
            End If
'TrimPartEnd
            .Sort Key1:=.Cells(1), Order1:=xlAscending, Header:=xlNo, OrderCustom:=1, MatchCase:=True, Orientation:=xlSortColumns
            .Cells(1).EntireColumn.Delete xlShiftToLeft
        End With
        Application.ScreenUpdating = True
    End If
    Set TrimTable = Range(Table.Rows(1), Table.Rows(Max))
End Function

この関数は訪問のたびに記入するような訪問者リストで訪問者の一番最初の訪問日のみを表にする、あるいは、色々な品物の価格を追記していく価格表で、最新の価格のみを表に残す等の利用方法が考えられます。
一方、全てのデータが一致する行を削除する方法として次のプロシージャが考えられます。

プログラム2

Sub TrimTable2(Optional Target As Range = Nothing)
    Dim temp As Range
    If Target Is Nothing Then Set Target = Selection.CurrentRegion
    With Target
        Application.ScreenUpdating = False
        Set temp = ActiveWorkbook.Worksheets.Add.Cells(1)
        .AdvancedFilter Action:=xlFilterCopy, CopyToRange:=temp, Unique:=True
        .Clear
        temp.CurrentRegion.Copy .Cells(1)
        Application.DisplayAlerts = False
        temp.Worksheet.Delete
        Application.DisplayAlerts = True
        Application.ScreenUpdating = False
    End With
End Sub

Excelに元からあるAdvancedFilterを用いました。このプログラムにはタイトル行(「製品」、「価格」等)が必要です。また、一番最初のデータが残るようですが、これを変更することは出来ません。
Excelの機能を使えば、簡単にマクロが記述できる例でした。ただし、自分の本当に欲しい要求にこたえてくれるとは限りません。
実際使う場面を想定して、プログラムを作ることが大切だと思います。作ったのは良いけど、いつ使うの?というプログラムではだめです。その点において、後のマクロは全ての項目ががまったく同じというような状況があまり思い浮かびません。(データ登録の日付や通し番号が打っているだけでアウトなのですから。)


目次に戻る

ファイルのバックアップを作成する(2002/1/18)

プログラム

'   関数名:SaveCopy
'   引数:  Path    String  (Optional)
'   説明:  現在使用しているファイルのバックアップファイルを作成します。
'       引数Pathで保存するフォルダを指定します。
'       (フォルダが存在しない場合はエラーとなります。)
'       省略した場合は元のファイルと同じフォルダに保存します。
'
'       バックアップファイルの名前は拡張子".xls"の前に".bak"をつけた
'       名前になります(それぞれ、定数XLS、DefaultBAKで定義しています)。
'       ただし、フォルダを指定した場合は".bak"は付きません。
'       また、一時ファイル等で拡張子".xls"が無い場合は自動で付加します。
'
'       もし、その名前が既に使用されていた場合は、".bak"に2から順に
'       数字をつけたファイル名を検索し、存在しないファイル名を探して
'       保存します(フォルダを指定した場合も同様に".bak"がつきます。
'       StartNumberに開始番号を定義しています。)
'
Sub SaveCopy(Optional Path As String = "")
    Const XLS As String = ".xls", DefaultBAK As String = ".bak"
    Const StartNumber As Integer = 2
    Dim fs As Object
    Dim NewFileName As String, NowPath As String, NowName As String
    Dim BAK As String
    Dim i As Integer
    Set fs = CreateObject("Scripting.FileSystemObject")
    If Path = "" Then
        NowPath = ActiveWorkbook.Path: BAK = DefaultBAK
    Else
        NowPath = Path: BAK = ""
    End If
    NowName = ActiveWorkbook.Name
    If InStr(1, NowName, XLS, vbTextCompare) = 0 Then
        NowName = NowName & XLS
    End If
    NewFileName = Replace(NowName, XLS, BAK & XLS)
    i = StartNumber
    BAK = DefaultBAK
    Do While fs.FileExists(fs.BuildPath(NowPath, NewFileName))
        NewFileName = Replace(NowName, XLS, BAK & i & XLS)
        i = i + 1
    Loop
    On Error Resume Next
    ActiveWorkbook.SaveCopyAs Filename:=fs.BuildPath(NowPath, NewFileName)
    If Err.Number <> 0 Then MsgBox "書き込みに失敗しました。"
    Err.Clear
    On Error GoTo 0
End Sub

説明
どんどん変更していくときに「元に戻せなくなったらやだな」と思うときがありません?別名で保存すると今度はそのファイル名で編集が続くことになって不便です。VBAには、SaveCopyAsという便利なメソッドがありますので、これを利用して作ってみました。このメソッドは同じ名前のファイルがあっても気にせず保存してしまうので、同名ファイルが存在した場合、回避するようにしてあります。保存先を引数で指定することも可能です。


目次に戻る

保護されたセルへの入力(2002/2/25)

プログラム

Private Function SheetLock(Sheet As Worksheet, Optional Switch As VBA.VbTriState = vbUseDefault, Optional PassWord As String = "") As Boolean
    On Error Resume Next
    Static DefaultProtect As Boolean
    Err.Clear
    Select Case Switch
    Case vbTrue
        If DefaultProtect = True Then
            Sheet.Protect PassWord:=PassWord
        End If
        SheetLock = True
    Case vbFalse
        DefaultProtect = Sheet.ProtectContents
        If DefaultProtect = True Then Sheet.Unprotect PassWord
        If Err.Number <> 0 Then
            SheetLock = False
        Else
            SheetLock = True
        End If
    Case Else
        SheetLock = DefaultProtect
    End Select
    On Error GoTo 0
End Function

保護されたシートへの入力はいくらVBAといえどもできません。シートがロックされていたら、ロックを解除し、書き込みが済んだ後にもう一度、ロックをかけるという手順を踏みます。ロックされていないシートの場合は、書き込み後もロックをかけないという判定も含めて、ひとつのマクロにしました。使用方法は次のとおりです。

プログラム2

Sub test()
    If SheetLock(ActiveSheet, vbFalse) = True Then
        Cells(1, 1) = 1
        Cells(1, 2) = Cells(1, 1) + Cells(1, 2)
        SheetLock ActiveSheet, vbTrue
    Else
        MsgBox "パスワードが解除できません。"
    End If
End Sub

vbFalseを指定するとシートのロックがかかっている場合はロックを解除します。シートのロックにパスワードがある場合、3番目の引数として、パスワードを渡します。解除に失敗するとFalseを返します。セルへの入力後、vbTrueを指定すると、vbFalseでマクロを実行したときにシートにロックがかかっていたならロックし、そうでなければロックしません。このときパスワードを指定することもできます。


目次に戻る

ロックされていないセルのみに移動(2002/3/9)

まず、セルのプロパティで「保護」の欄に「ロック」という項目があるのをご存知ですか? 通常はチェックが入った状態ですが、このチェックを消してシートに保護を掛けると、 シートが保護された状態で、そのセルの値を変更することができます。
そして、値を入力した後にTabキーを押すとなんと、次の保護されていないセルまで、 一気にカーソルが飛ぶんですね。非常に便利な機能です。
ただ、多分、皆さんは入力した後にリターンキーを押しますよね。するとやはり、 ロックしているセルにカーソルが移ってしまうんです。これは困ります。
そこで次のプログラムです。

プログラム(シートのコードとして記入)

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Application.EnableEvents = False
    With Target.Cells(1)
        If .Locked = True And _
        .Worksheet.ProtectContents = True And _
        Application.MoveAfterReturn = True Then
            Select Case Application.MoveAfterReturnDirection
            Case xlToRight: .Offset(0, -1).Next.Select
            Case xlToLeft:  .Offset(0, 1).Next.Select
            Case xlUp:      .Offset(1, 0).Next.Select
            Case xlDown:    .Offset(-1, 0).Next.Select
            End Select
        End If
    End With
    Application.EnableEvents = True
End Sub

ちなみにシートのコードは各シートのタブ(下の名前の書いてあるところ)を右クリックして出てくる「コードの表示」をクリックすれば記入することができます。
移動先のセルがロックされていてかつ、シートがロックされていてかつ、リターンを押したときに自動的に次のセルに移動する設定であるとき、リターンキーが押されたとみなして移動前のセルに戻ってタブキーを押した時に移動するセルに移動します。
欠点としては、リターンキーで移動した先が意図しない記入可能なセルの場合、適切なセルに戻りません。
ちなみにシートがロックされていなくても機能させたい場合は5行目(.Worksheet以下)を削除してください。


目次に戻る

セルの幅、高さの調整(2002/3/20)

行の高さや列の幅を一番大きいセルに合わせる方法をご存知ですか。
例えば、列を選択して列タイトル(例えばA)などの幅調節ができる部分で ダブルクリックすると、選んだ全部の列について自動的に調節してくれます。 しかし、例えば表をきれいにしたいんだけど、キャプションが横長で、 それに合わせてセルを横長にしたくない。 というときには、以下のマクロが有効です。

プログラム

Sub SelectedAutoFit()
    Dim SA As Range
    For Each SA In Selection.Areas
        With SA
            .Columns.AutoFit
            .Rows.AutoFit
        End With
    Next SA
End Sub

具体的な使用方法は基準となるセルを選択しておいた後、このマクロを呼び出します。 選択セルを含む行列の幅、高さが選択セルだけを基準に調節されます。


目次に戻る

セルに合わせた図形・グラフ等の拡大・縮小(2002/5/22)

以前、さわりだけ書いたマクロです。図形やグラフなどを一度切り取ってから、貼り付けたいセル範囲を選択した上で以下のプログラムを実行すると、そのセル範囲に合わせた大きさで図形等を貼り付けます。2番目のプログラムは図形の縦横の比率を変えずに貼り付けます。大きさの調整で困っていた方はぜひお使いください。
ちなみにNyama.xlsに収録済みです。

プログラム

Sub CellSizePaste()
    On Error Resume Next
    Dim nowCells As Range
    Dim nowShapeRange As ShapeRange
    Set nowCells = Selection
    ActiveSheet.Paste
    If TypeName(Selection) = "ChartArea" Then
        Set nowShapeRange = Selection.Parent.Parent.ShapeRange
    Else
        Set nowShapeRange = Selection.ShapeRange
    End If
    With nowShapeRange
        .LockAspectRatio = msoFalse
        .Height = nowCells.Height
        .Width = nowCells.Width
        .Top = nowCells.Top
        .Left = nowCells.Left
    End With
End Sub

Sub CellSizePaste2()
    On Error Resume Next
    Dim nowCells As Range
    Dim nowShapeRange As ShapeRange
    Dim mag As Double
    Set nowCells = Selection
    ActiveSheet.Paste
    If TypeName(Selection) = "ChartArea" Then
        Set nowShapeRange = Selection.Parent.Parent.ShapeRange
    Else
        Set nowShapeRange = Selection.ShapeRange
    End If
    With nowShapeRange
        mag = Application.WorksheetFunction.Min(CDbl(nowCells.Height) / .Height, CDbl(nowCells.Width) / .Width)
        .ScaleHeight mag, False
        .ScaleWidth mag, False
        .Top = nowCells.Top + (nowCells.Height - .Height) / 2
        .Left = nowCells.Left + (nowCells.Width - .Width) / 2
    End With
End Sub


目次に戻る

座標の変換(2002/6/19)

下のような表があるとします。B2-B4は入力データ、B5は数式=SUM(B2:B4)が入ってるとします。この場合、C列にはどんな数式を書きますか?
ABC
1No.Value%
211020
321530
432550
5Total50100

このような場合、分母(合計)の入ったセルはB5ですから、このセルに対する絶対参照を使った数式=B2/$B$5*100をC2に書きます。あとはC4までドラッグするとB5の部分(絶対参照)は変化せず、B2の部分(相対参照)のみB3,B4と変化していきます。非常に便利ですね。 ところが、この表全体をコピーペーストして別の場所で使おうとすると、数式はB2の部分のみ変化して、B5はそのまま参照してしまいます。これでは不便ですね。
絶対参照は表を作るときは便利ですが、そのあとは特に絶対参照である必要はなく、むしろ相対参照のほうが便利な場合が多いです。そこで、絶対参照を相対参照に変更するマクロを作りました。

プログラム

Sub AbsoluteToRelative()
    Dim a As Range, r As Range
    If TypeName(Selection) = "Range" Then
        For Each a In Selection.Areas
        For Each r In a.Cells
            If r.HasFormula Then
                r.FormulaLocal = Replace(r.FormulaLocal, "$", "")
            End If
        Next r
        Next a
    Else
        MsgBox "セルを選択してから実行してください。", vbOKOnly, "絶対座標の相対座標への変換"
    End If
End Sub

単に$を抜いているだけです。でも便利でしょ?


目次に戻る

数字を抜き取る(2002/9/21)

別のアプリケーションからExcelに数値データを取り込む際に数値以外のものが、同じセルに取り込まれることがありますよね。具体的には、下のとおりです。

セルのデータ欲しい数値
(変換後のセル)
分子量: 233.3233.3
税込み 1,233円1233
12.22cm212.22
数値がない場合は無視数値がない場合は無視

こんなとき、欲しい数値だけ抜き出し、セルに代入する。数値がない場合はセルに変更を加えない。そんなプログラムを作りました。

プログラム

Sub getNumber()
    Dim r As Range, a As Range
    Dim i As Integer, j As Integer
    Dim txt As String
    If TypeName(Selection) <> "Range" Then Exit Sub
    For Each a In Selection.Areas
    For Each r In a.Cells
        txt = r.Text
        For i = 1 To Len(txt)
            If IsNumeric(Mid(txt, i, 1)) Then
                txt = Right(txt, Len(txt) - i + 1)
                For j = Len(txt) To 1 Step -1
                    If IsNumeric(Left(txt, j)) Then
                        r = CDbl(Left(txt, j))
                        Exit For
                    End If
                Next j
                Exit For
            End If
        Next i
    Next r
    Next a
End Sub

ちょっと見苦しいプログラムです。私ならこうする。というプログラムがありましたら、ご連絡ください。この場で紹介したいと思います。プログラムの条件としては、上の表の変換が正しくできることとします。


目次に戻る

フロッピーのデータをハードディスク上で扱う(2002/9/27)

フロッピーディスク上のワークブックを直接読み込んで編集すると、(1)セーブのたびに待たされる、(2)Excelのハングアップする可能性が高くなる(と思われる)、という問題があるので、

  1. フロッピーディスクからワークブックを読み込んだら、
  2. 一時保存フォルダに一時保存ブックを作成し、
  3. 作業は一時保存ブックに対して行う。
  4. ワークブックを閉じる際に一時保存ブックかどうかを確認し、
  5. そうである場合は、セーブ内容をフロッピーディスクに反映させる。
という動作をするマクロを考えました。
VBAをそれを記述しているワークブック以外のイベント(ワークブックの読み込み、保存、全てのワークブック上のワークシートのセルの変更など)に応答するマクロを記述しようとすると、クラスモジュールを扱う必要があります。

プログラム

---ThisWorkbook内---
Private Sub Workbook_Open()
    FloppyDataModule.init_FloppyData    '初期化の呼び出し
End Sub
---標準モジュール(FloppyDataModuleとしてください)内---
Public X As New FloppyDataClass 'アプリケーションフック

Public Sub init_FloppyData()
    Set X.App = Excel.Application
    X.init
End Sub
---クラスモジュール(FloppyDataClassとしてください)内---
Public WithEvents App As Application

Private Const FloppyLetter As String = "A:"
Private Const FullNameColumn As Integer = 1
Private Const NameColumn As Integer = 2
Private Const TempColumn As Integer = 3
Private index As Integer
Private ListSheet As Worksheet
Private fs As Object

Public Sub init()   '初期化
    index = 0
    Set ListSheet = ThisWorkbook.Worksheets(1)
    Set fs = CreateObject("Scripting.FileSystemObject")
End Sub

Private Sub App_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
    Dim fileindex As Integer
    If index = 0 Then   '一時保存ファイルが存在しない場合
        Cancel = False  'ファイルを閉じる。
        Exit Sub        '処理を終了する。
    End If
    
    On Error Resume Next
    Err.Clear
    '一時保存ファイルと一致するかどうか確認。
    fileindex = Application.WorksheetFunction.Match(Wb.Name, _
        Range(ListSheet.Cells(1, NameColumn), ListSheet.Cells(index, NameColumn)), 0)
    If Err.Number <> 0 Then '一致しない場合
        Cancel = False      'ファイルを閉じる。
        Exit Sub            '処理を終了する。
    End If
    On Error GoTo 0
    
    '一時保存ファイルを元のファイルに上書き
    '一時保存ファイルからリネームして編集した場合、
    'リネーム以前のファイルの編集が反映される。
    On Error Resume Next
    fs.DeleteFile ListSheet.Cells(fileindex, FullNameColumn), True
    On Error GoTo 0
    Wb.SaveAs ListSheet.Cells(fileindex, FullNameColumn)
    
    '一時保存ファイルを削除
'    On Error Resume Next
'    fs.DeleteFile ListSheet.Cells(fileindex, TempColumn), True
'    On Error GoTo 0
    
    'リストからファイル名等を削除
    ListSheet.Cells(fileindex, FullNameColumn) = ""
    ListSheet.Cells(fileindex, NameColumn) = ""
    ListSheet.Cells(fileindex, TempColumn) = ""
End Sub

Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
    If (Left(Wb.Path, 2) = FloppyLetter) Then   'フロッピーであることの確認
        'indexを1増加し、リストにファイル名等を入力
        index = index + 1
        ListSheet.Cells(index, FullNameColumn) = Wb.FullName
        ListSheet.Cells(index, NameColumn) = Wb.Name
        ListSheet.Cells(index, TempColumn) = fs.getSpecialFolder(2) & "\" & Wb.Name
        '一時保存ファイルを作成し、そのファイルを編集するようにする。
        '一時保存ファイルと同名のファイルがある場合、削除する。
        On Error Resume Next
        fs.DeleteFile ListSheet.Cells(index, TempColumn), True
        On Error GoTo 0
        Wb.SaveAs ListSheet.Cells(index, TempColumn)
    End If
End Sub

ThisWorkbook内はこのワークブックにのみ影響するイベントを記述する部分です。今回はワークブックが開かれたら、初期化ルーチンを呼び出すということを行っています。
標準モジュール内ではアプリケーションが支配する全てのイベントを記述することができるクラスの実体を生成します。クラスモジュール内で
Public WithEvents App As Application
という特別な変数を作り、これに
Set X.App = Excel.Application
でアプリケーションへの参照をセットしてやります。これにより、アプリケーションでイベントが起きるたびにクラスが呼び出され、対応する関数を(あれば)実行するようになります。今回は、ワークブックを開いたときと閉じる前に呼び出されて処理を行うことになります。
関数本体は標準モジュールに記述して、クラスモジュールはイベントのキャッチのみ行う。という記述の方法もありますが、オブジェクト指向のカプセル化という概念から考えると、処理はクラスモジュール内で記述するのが正しいような気がします。
動作が気に入ったらアドインにしてしまいましょう。方法はここに書いてあります。


目次に戻る

数式を値に置換する(2002/12/5)

数式で得られた数値をそのまま固定値としてセルに代入します。

プログラム

Sub ConstValue(Optional dummy As Integer = 0)
    On Error Resume Next
    Selection.Copy
    Selection.PasteSpecial Paste:=xlValues, Operation:=xlNone, SkipBlanks:= _
        False, Transpose:=False
    Application.CutCopyMode = False
End Sub


目次に戻る

シートをCSV出力(2002/12/21)

エクセルで計算したデータをカンマ区切りのテキストファイル(CSV)にして他のプログラムで利用する必要がよく生じます。通常の「名前を付けて保存」では以降の編集ファイルもCSVファイルに変わってしまい不便です。そこで、表示しているワークシートをCSVファイルとして書き出すマクロを作りました。

プログラム

Sub WriteOutToCSV()
    Dim file As Variant
    Dim NewBook As Workbook
    Dim NowSheet As Worksheet
    Set NowSheet = ActiveSheet
    file = Application.GetSaveAsFilename(ActiveSheet.Name & ".csv", "カンマ区切り形式 (*.csv), *.csv")
    If file = False Then Exit Sub
    Set NewBook = Workbooks.Add
    NowSheet.Copy before:=NewBook.Worksheets(1)
    On Error Resume Next
    NewBook.Worksheets(1).SaveAs Filename:=file, FileFormat:=xlCSV
    On Error GoTo 0
    Application.DisplayAlerts = False
    NewBook.Close
    Application.DisplayAlerts = True
    NowSheet.Activate
End Sub

選択しているシート全てを書き出す方法を考えてみてください。新しいNyamaツールに入っているものは選択シート全て(グラフシートは除きます)を書き出すようになっています。


目次に戻る

にゃま夫のへや   ・Office VBA   ・おもちゃ箱   ・iαppli
ホームページに戻る。
無断転載を禁じます。
にゃま夫