Excel VBAでファイル拡張子を一括変更する方法|プレビューと安全な実行手順

Excel VBAでは、フォルダ内のファイル名をまとめて変更できます。ただし、拡張子の変更はファイルの改名であり、データ形式の変換ではありません。この記事では、内容が既にCSV形式になっているテキストファイルを想定し、.txtを.csvに改名する例を紹介します。

いきなり大量の原本を変更せず、原本とバックアップを保管したうえで、テスト用コピーだけを対象にしてください。以下のコードは、単一フォルダ・最大100件に限定し、「変更予定の一覧を作る」「確認した計画を実行する」の2段階で動きます。サブフォルダは処理しません。

目次

拡張子の一括変更でできること・できないこと

  • できること:売上.txtを売上.csvにするなど、ファイル名の最後の拡張子を変更すること。
  • できないこと:タブ区切りをカンマ区切りに変える、文字コードを変える、画像やOffice文書の保存形式を変えること。
  • 注意:.xlsを.xlsxに、.pngを.jpgに改名しても、その形式のデータにはなりません。

Microsoftも、拡張子を変更してもファイル形式は変換されないと説明しています。形式を変えたい場合は、対応するアプリの保存・エクスポート機能を使います。Excelブックでは、拡張子だけの変更ではなく、目的に合ったFileFormatを指定するWorkbook.SaveAsなどが別途必要です。Microsoft:Windowsのファイル名拡張子/Workbook.SaveAs

実行前の準備と対象範囲

  1. 対象はWindows版デスクトップExcelです。以下はWindowsのFileSystemObjectを使う例であり、Mac版やブラウザー版での実行手順ではありません。
  2. 元フォルダのバックアップを別の場所に保管し、さらに少数のテスト用コピーを作成します。原本・唯一のバックアップには実行しないでください。
  3. ファイルを開いているアプリを閉じます。共有フォルダ、同期中のクラウドフォルダ、他の処理が更新している場所ではなく、ローカルの通常フォルダで試します。
  4. エクスプローラーで拡張子を表示し、内容が想定どおりのCSVであることをテキストエディターなどで確認します。名前の変更だけでは区切り文字・文字コードは変わりません。
  5. マクロ用の新しいブックを.xlsmで保存し、Alt+F11 →「挿入」→「標準モジュール」に、次のコード全体を貼り付けます。マクロ用ブックは対象フォルダの外に置きます。

マクロの設定を「すべて有効」にする必要はありません。内容を確認した自作ブックだけを、組織のポリシーに従って実行してください。「VBAプロジェクトオブジェクトモデルへのアクセスを信頼する」も、このコードのために有効化する必要はありません。管理されたPCで実行を禁止されている場合は、制限を回避せず管理者に確認します。Microsoft:マクロの有効化・無効化と注意点

FileSystemObjectの参照設定は必要?

この例はDim fso As ObjectとCreateObject("Scripting.FileSystemObject")を使う遅延バインディングです。追加の「Microsoft Scripting Runtime」参照設定は不要です。Dim fso As Scripting.FileSystemObjectのように型を指定する早期バインディングとは区別してください。Microsoft:CreateObject関数

プレビューと実行を分けたVBAコード

まずPreviewExtensionRenameを実行します。対象フォルダの直下にある.txt/.TXTだけを拾い、変更前・変更後のフルパス、サイズ、更新日時を新規ブックに一覧化します。この段階ではファイルを改名しません。拡張子なしや他の拡張子、サブフォルダ内のファイルは対象外です。読み取り専用・隠し・システム・リンク属性のファイルも除外します。

変更先に同名のファイルまたはフォルダがある場合、変更先が重複する場合、対象が100件を超える場合は、プレビューを中止します。既存ファイルの上書き・削除、同名への連番付与、サブフォルダへの再帰処理は行いません。

Option Explicit

' 対象を固定: 単一フォルダの txt → csv。内容の変換は行いません。
Private Const MAX_FILES As Long = 100
Private mPlan As Collection
Private mReport As Worksheet

Public Sub PreviewExtensionRename()
    Dim fso As Object, folder As Object, file As Object, seen As Object
    Dim plan As Collection, report As Worksheet, picker As FileDialog
    Dim folderPath As String, newPath As String, item As Variant
    Dim i As Long, skipped As Long
    On Error GoTo Failed
    Set mPlan = Nothing
    Set mReport = Nothing
    Set plan = New Collection
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set seen = CreateObject("Scripting.Dictionary")
    seen.CompareMode = vbTextCompare

    Set picker = Application.FileDialog(msoFileDialogFolderPicker)
    picker.Title = "原本ではなく、バックアップから作ったテスト用コピーを選択"
    If picker.Show <> -1 Then Exit Sub
    folderPath = picker.SelectedItems(1)
    If fso.GetParentFolderName(folderPath) = "" Then
        Err.Raise vbObjectError + 1000, , "ドライブ直下は選択できません。"
    End If
    Set folder = fso.GetFolder(folderPath)
    If (folder.Attributes And 1024) <> 0 Then
        Err.Raise vbObjectError + 1005, , "リンク先のフォルダは対象にできません。"
    End If

    ' 列挙中は名前を変更せず、変更予定をメモリに保存します。
    For Each file In folder.Files
        If StrComp(fso.GetExtensionName(file.Name), "txt", vbTextCompare) = 0 Then
            If (file.Attributes And 1031) <> 0 Then
                skipped = skipped + 1  ' 読み取り専用・隠し・システム・リンクは除外
            Else
                If fso.GetBaseName(file.Name) = "" Then
                    Err.Raise vbObjectError + 1001, , "名前本体が空です: " & file.Name
                End If
                newPath = fso.BuildPath(folderPath, fso.GetBaseName(file.Name) & ".csv")
                If fso.FileExists(newPath) Or fso.FolderExists(newPath) Then
                    Err.Raise vbObjectError + 1002, , "変更先が既に存在します: " & newPath
                End If
                If seen.Exists(newPath) Then
                    Err.Raise vbObjectError + 1003, , "変更先が重複します: " & newPath
                End If
                seen.Add newPath, True
                plan.Add Array(file.Path, newPath, CDbl(file.Size), CDbl(file.DateLastModified))
                If plan.Count > MAX_FILES Then
                    Err.Raise vbObjectError + 1004, , "対象が100件を超えました。フォルダを分けてください。"
                End If
            End If
        End If
    Next file
    If plan.Count = 0 Then
        MsgBox "対象は0件です。属性による除外: " & skipped & "件", vbInformation
        Exit Sub
    End If

    ' 既存シートを消さず、新規ブックに確認用一覧を作成します。
    Set report = Workbooks.Add(xlWBATWorksheet).Worksheets(1)
    report.Name = "拡張子変更予定"
    report.Columns("A:B").NumberFormat = "@"
    report.Range("A1:E1").Value = Array("変更前", "変更後", "サイズ(bytes)", "更新日時", "結果")
    For i = 1 To plan.Count
        item = plan(i)
        report.Cells(i + 1, 1).Value2 = item(0)
        report.Cells(i + 1, 2).Value2 = item(1)
        report.Cells(i + 1, 3).Value2 = item(2)
        report.Cells(i + 1, 4).Value2 = item(3)
        report.Cells(i + 1, 5).Value2 = "未実行"
    Next i
    report.Columns("D").NumberFormat = "yyyy-mm-dd hh:mm:ss"
    report.Columns("A:B").ColumnWidth = 60
    report.Columns("C:E").AutoFit
    report.Rows(1).Font.Bold = True
    Set mReport = report
    Set mPlan = plan
    MsgBox "プレビューのみ: " & plan.Count & "件。ファイルは変更していません。" & vbCrLf & _
           "属性による除外: " & skipped & "件。全行を確認し、一覧を保存してください。", vbInformation
    Exit Sub
Failed:
    Set mPlan = Nothing
    MsgBox "プレビューを中止しました。ファイルは変更していません。" & vbCrLf & _
           Err.Number & ": " & Err.Description, vbExclamation
End Sub

Public Sub ApplyExtensionRename()
    Dim fso As Object, plan As Collection, item As Variant
    Dim i As Long, done As Long, oldPath As String, newPath As String
    Dim errorText As String
    On Error GoTo Failed
    If mPlan Is Nothing Then
        MsgBox "先に PreviewExtensionRename を実行してください。", vbExclamation
        Exit Sub
    End If
    Set plan = mPlan
    Set mPlan = Nothing  ' 成功・中止・失敗のいずれでも、再実行には新しいプレビューが必要
    Set fso = CreateObject("Scripting.FileSystemObject")

    ' 1件も変更する前に、一覧と全対象を再確認します。
    For i = 1 To plan.Count
        item = plan(i)
        oldPath = CStr(item(0))
        If CStr(mReport.Cells(i + 1, 1).Value2) <> CStr(item(0)) Or _
           CStr(mReport.Cells(i + 1, 2).Value2) <> CStr(item(1)) Then
            Err.Raise vbObjectError + 1010, , "一覧が編集されています。プレビューを作り直してください。"
        End If
        CheckRenameItem fso, item
    Next i
    If MsgBox("全行を確認し、原本・バックアップを別に保管しましたか?" & vbCrLf & _
              plan.Count & "件のテスト用コピーを改名します。形式変換はしません。" & vbCrLf & _
              "途中で失敗しても、完了分は自動では戻りません。", _
              vbYesNo + vbExclamation + vbDefaultButton2) <> vbYes Then Exit Sub

    For i = 1 To plan.Count
        item = plan(i)
        oldPath = CStr(item(0))
        newPath = CStr(item(1))
        CheckRenameItem fso, item  ' 各変更の直前にも再確認
        mReport.Cells(i + 1, 5).Value2 = "処理中(要確認)"
        Name oldPath As newPath
        done = done + 1
        mReport.Cells(i + 1, 5).Value2 = "改名済み"
    Next i
    MsgBox done & "件を改名しました。一覧を再保存し、内容を確認してください。", vbInformation
    Exit Sub
Failed:
    errorText = Err.Number & ": " & Err.Description
    Set mPlan = Nothing
    MsgBox "中止しました。改名完了: " & done & "件。自動復元はしません。" & vbCrLf & _
           "確認対象: " & oldPath & vbCrLf & errorText & vbCrLf & _
           "一覧と実際のファイル名を確認し、同じ操作を繰り返さないでください。", vbCritical
End Sub

Private Sub CheckRenameItem(ByVal fso As Object, ByVal item As Variant)
    Dim file As Object
    If Not fso.FileExists(CStr(item(0))) Then
        Err.Raise vbObjectError + 1020, , "元ファイルが見つかりません: " & item(0)
    End If
    If fso.FileExists(CStr(item(1))) Or fso.FolderExists(CStr(item(1))) Then
        Err.Raise vbObjectError + 1021, , "変更先が存在します: " & item(1)
    End If
    Set file = fso.GetFile(CStr(item(0)))
    If (file.Attributes And 1031) <> 0 Or CDbl(file.Size) <> CDbl(item(2)) Or _
       CDbl(file.DateLastModified) <> CDbl(item(3)) Then
        Err.Raise vbObjectError + 1022, , "元ファイルの状態が変わりました: " & item(0)
    End If
End Sub

一覧を確認してから実行する手順

  1. Alt+F8からPreviewExtensionRenameを実行し、テスト用コピーフォルダを選びます。新しく開いた「拡張子変更予定」シートが今回の一覧です。以前の一覧と取り違えないでください。
  2. 変更前・変更後の全行を確認し、件数・フォルダ・ファイル名が意図どおりか照合します。一覧は並べ替え・編集せず、対象フォルダの外に保存します。保存した一覧だけから実行を再開する仕組みではありません。
  3. 同じExcelセッションで、マクロ用ブックと一覧ブックを開いたまま、Alt+F8からApplyExtensionRenameを実行します。確認画面の既定値は「いいえ」です。原本とバックアップが別にあることを確かめた場合だけ「はい」を選びます。
  4. 完了後は「結果」列と実際のファイル名を確認し、一覧を再保存します。代表的なファイルを開き、利用先のアプリで正しく読めるか、文字化けや区切りの問題がないかを確認します。

実行するのは、直前のプレビューでメモリに保存した計画だけです。一覧のセルを書き換えて対象を追加することはできません。プレビュー後に増えたファイルも、勝手には追加されません。Excelを閉じた場合、VBAがリセットされた場合、実行を中止・キャンセルした場合は、改めてプレビューから始めてください。

このサンプルの拡張子はtxt → csvに固定しています。「何でも同じ拡張子にする」コードではありません。別の拡張子に改修する場合も、空文字・ワイルドカード・パスを受け付ける設計にせず、同じ拡張子への改名や変更先の重複を防ぐ検査が必要です。まず固定例を数件で確認してください。

コードの仕組みと、旧来の書き方で注意する点

  • 列挙中に改名しない:folder.Filesで対象の情報を収集し終えてから、保存した計画を処理します。元ファイル群を列挙しながら次々に名前を変える構成を避けています。
  • 拡張子は最後の部分で判定:GetExtensionNameとGetBaseNameを使うため、売上.2026.txtの予定名は売上.2026.csvです。ファイル名全体に対するReplaceは使いません。
  • フルパスで改名:実行部分はName oldPath As newPathです。これはVBAのステートメントであり、Fileオブジェクトのfile.Nameプロパティとは別です。file.Nameにフルパスを代入する書き方と混同しないでください。
  • 参照設定の不要な呼び出し:FSOは遅延バインディングを使っています。MoveFileを使う別の実装では、引数を位置で渡すfso.MoveFile oldPath, newPathという形にし、名前付き引数を前提にしないでください。

Nameは変更先が既に存在すると使えず、開いているファイルの改名もエラーになります。また、Dirは検索状態を保持するため、列挙の途中で別のDir(パス)を呼んだり再帰呼び出しに使ったりする実装には注意が必要です。今回のコードでは存在確認もFSOに統一しています。Nameステートメント/FSOのNameプロパティ/Dir関数

途中で失敗した場合と、復元の限界

この処理はトランザクションではありません。たとえば3件目で権限エラーが出た場合、既に成功した2件は改名後の状態で残ります。エラーが出たからといって、全件が未変更とは限りません。MoveFileでも、途中の変更を自動的にロールバックする動作はありません。Microsoft:MoveFileメソッド

コードは、変更前に全対象の存在・変更先の衝突・サイズ・更新日時・属性を再検査し、各件の直前にも確認します。それでも検査後の他プロセスによる変更を完全には防げません。サイズと更新日時は内容の完全一致を証明するものでもありません。そのため、他のアプリが変更しないテスト用コピーでの使用を前提にしています。リンク属性の検査は選択フォルダと各ファイルだけで、親フォルダに含まれるジャンクションまでは検査しません。リンクやジャンクションを経由しない通常のローカル作業フォルダを選んでください。

  • 「改名済み」:その行の改名が成功し、結果の書き込みまで終わっています。
  • 「処理中(要確認)」:改名直前に書いた印です。異常終了などがあった場合は、実際の変更前・変更後の名前を確認してください。
  • 「未実行」:通常の制御では改名を開始していません。ただしExcelの強制終了などで一覧が保存されていない場合、保存済み一覧だけでは判断できません。

失敗したときは同じマクロを連打せず、エラーメッセージ、一覧、実際のファイル名を照合します。戻す場合も、変更前の名前に別ファイルがないことを確認したうえで個別に戻すか、失敗したテスト用コピーを別に保管して、手を加えていないバックアップから新しいテスト用コピーを作り直します。「全部の.csvを.txtへ戻す」という逆向きの一括処理は、もともとあったCSVまで変えるため避けてください。

このサンプルはOn Error GoToで中止理由を表示し、成功した件数を通知します。On Error Resume Nextでエラーを無視して「全件完了」と表示する処理ではありません。対象の内容は書き換えませんが、改名の影響やバックアップの必要性がなくなるわけではありません。

テスト用コピーで確認する項目

  • a.txt、B.TXT、売上.2026.txt:最後の拡張子だけが.csvになる予定か。
  • README、image.png、サブフォルダ内のa.txt:今回の対象に含まれないか。
  • a.txtとa.csv、またはa.csvというフォルダ:衝突を検出し、何も変更せず中止するか。
  • プレビュー後に元ファイルの内容・更新日時を変える、変更先ファイルを新しく置く:実行前検査で止まるか。
  • 対象ゼロ、101件以上、読み取り専用・隠しファイル、アクセスできない場所:結果や中止理由を確認できるか。
  • 実行確認で「いいえ」を選ぶ:改名されず、再実行時に新たなプレビューを求められるか。

これらは利用環境で確認するためのチェック項目です。この記事のコードについて、実際のExcel環境での動作確認済みを保証するものではありません。導入時はVBEのコンパイル確認と、数件の使い捨てテストデータによる検証から始めてください。

コメントへの回答:サブフォルダも対象にしたい場合

コメント

はじめまして
私も中小企業のメンバー2名の情シス部署で日々戦々恐々と勤しんでいます。
いろいろな記事を拝見させていただきながら、勉強させていただいています。
VBAは初心者ですが、同じ処理を大量に行うためにはVBAの活用が必要と思い、
調べたり、試行錯誤しながら少しずつエクセルに組み込んで試しています。

この記事は丁度必要とする処理であり、ぜひ参考にさせていただきたいのですが
処理したいファイルがサブフォルダを含むフォルダ内へ分散して存在しているため、
サブフォルダも含めて変換処理をしたいです。
もし、可能であれば「フォルダ内のファイルを再帰的に処理」の部分について
具体的なコード例の教授をお願いできたらうれしい限りです。

コメントありがとうございます。サブフォルダに分散したファイルを扱う場合も、最初に対象と変更予定を一覧化することが大切です。ただし、深い階層、アクセスできないフォルダ、リンク・ジャンクション、共有フォルダなどが混ざると、再帰処理の範囲や失敗時の扱いが複雑になります。「階層が深くても問題ない」とは言い切れません。

そのため、この記事では無制限に再帰して改名するコードではなく、フォルダを1つずつ選び、最大100件ずつ確認する方法に改めています。各サブフォルダを個別に選べば、同じ確認手順を適用できます。全階層を対象にする必要がある場合は、まず読み取り専用の一覧作成を別に行い、対象範囲・件数上限・除外するフォルダ・復旧方法を決めてから実処理を設計してください。

なお、以前のようにfile.Name = newFilePathへフルパスを渡す書き方は、Nameプロパティの用途と合いません。今回の例では、フルパスを指定できるVBAのName oldPath As newPathに統一しています。また、ここでも行うのは「変換」ではなく「改名」です。

まとめ

Excel VBAで拡張子を一括変更するときは、改名と形式変換を区別し、対象を限定したプレビューから始めましょう。元ファイルとバックアップを保管し、テスト用コピーで全行確認・衝突検査・結果確認まで行うことが重要です。エラーで止まっても完了分は自動では戻らないため、一覧と実ファイルを照合できる状態を残してください。

参考資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメント一覧 (2件)

  • はじめまして
    私も中小企業のメンバー2名の情シス部署で日々戦々恐々と勤しんでいます。
    いろいろな記事を拝見させていただきながら、勉強させていただいています。
    VBAは初心者ですが、同じ処理を大量に行うためにはVBAの活用が必要と思い、
    調べたり、試行錯誤しながら少しずつエクセルに組み込んで試しています。

    この記事は丁度必要とする処理であり、ぜひ参考にさせていただきたいのですが
    処理したいファイルがサブフォルダを含むフォルダ内へ分散して存在しているため、
    サブフォルダも含めて変換処理をしたいです。
    もし、可能であれば「フォルダ内のファイルを再帰的に処理」の部分について
    具体的なコード例の教授をお願いできたらうれしい限りです。

コメントする

目次