Excel:在不离开当前Excel表单的情况下将工作表导出为CSV文件的宏

49

这里有很多关于如何创建一个宏来将工作表保存为CSV文件的问题。所有答案都使用了SaveAs,例如从SuperUser网站中的这个回答。它们基本上是建议创建一个如下的VBA函数:

Sub SaveAsCSV()
    ActiveWorkbook.SaveAs FileFormat:=clCSV, CreateBackup:=False
End Sub
这是一个很好的答案,但我想要进行导出而不是另存为。执行SaveAs会导致两个烦恼:
  • 我的当前工作文件变成了CSV文件。 我想继续在原始的.xlsm文件中工作,但将当前工作表的内容导出为具有相同名称的CSV文件。
  • 弹出对话框要求我确认是否要重写CSV文件。

有可能只导出当前工作表作为文件,但继续在我的原始文件中工作吗?


4
我认为你需要创建一个工作簿,将你的表格复制到其中,另存为csv格式,并关闭该工作簿。 - gtwebb
@gtwebb:你能帮我吗?我的VBA知识非常基础。 - neves
1
使用此问题中的“SeanC”的第二个答案:http://stackoverflow.com/questions/26178913/saving-excel-worksheet-to-csv-with-file-name-from-a-cell-using-a-macro?rq=1 - Nathan Clement
请按照Tony Dallimore的回答,创建并编写文本文件,不要使用工作簿功能。创建和编写文本文件 - OldUgly
尝试这个 https://exceldevelopmentplatform.blogspot.com/2019/08/vba-export-worksheet-to-csv.html - S Meaden
7个回答

49

@NathanClement 更快一些。不过这里是完整的代码(略微更为详细):

Option Explicit

Public Sub ExportWorksheetAndSaveAsCSV()

Dim wbkExport As Workbook
Dim shtToExport As Worksheet

Set shtToExport = ThisWorkbook.Worksheets("Sheet1")     'Sheet to export as CSV
Set wbkExport = Application.Workbooks.Add
shtToExport.Copy Before:=wbkExport.Worksheets(wbkExport.Worksheets.Count)
Application.DisplayAlerts = False                       'Possibly overwrite without asking
wbkExport.SaveAs Filename:="C:\tmp\test.csv", FileFormat:=xlCSV
Application.DisplayAlerts = True
wbkExport.Close SaveChanges:=False

End Sub

如果我需要UTF8格式的CSV,为什么某些Excel版本不支持它?这似乎很奇怪,也是一个大问题。 - andQlimax
要获取UTF8编码,请在SaveAs调用中使用FileFormat:=xlCSVUTF8 - undefined

27

这几乎是我想要的 @Ralph,但是这里是最佳答案,因为它解决了您代码中的一些烦恼:

  1. 它导出当前工作表,而不仅仅是硬编码的名为“Sheet1”的工作表;
  2. 它将导出到以当前工作表命名的文件中
  3. 它尊重区域分隔符。
  4. 您可以继续编辑xlsx文件,而不是编辑导出的CSV。

为了解决这些问题并满足我的所有需求,我改编了这里的代码。我对其进行了清理以使其更易读。

Option Explicit
Sub ExportAsCSV()
 
    Dim MyFileName As String
    Dim CurrentWB As Workbook, TempWB As Workbook
     
    Set CurrentWB = ActiveWorkbook
    ActiveWorkbook.ActiveSheet.UsedRange.Copy
 
    Set TempWB = Application.Workbooks.Add(1)
    With TempWB.Sheets(1).Range("A1")
      .PasteSpecial xlPasteValues
      .PasteSpecial xlPasteFormats
    End With        

    Dim Change below to "- 4"  to become compatible with .xls files
    MyFileName = CurrentWB.Path & "\" & Left(CurrentWB.Name, Len(CurrentWB.Name) - 5) & ".csv"
     
    Application.DisplayAlerts = False
    TempWB.SaveAs Filename:=MyFileName, FileFormat:=xlCSV, CreateBackup:=False, Local:=True
    TempWB.Close SaveChanges:=False
    Application.DisplayAlerts = True
End Sub

请注意上面代码的一些特点:

  1. 它只能在当前文件名有4个字母的情况下工作,比如.xlsm。不能在.xls旧版excel文件中使用。对于3个字符的文件扩展名,在上面的代码中设置MyFileName时,必须将- 5更改为- 4
  2. 作为一个副作用,您的剪贴板将被当前工作表的内容替换。

编辑:添加Local:=True以保存带有本地CSV分隔符的文件。


3
  1. TempWB.Close False 改为 TempWB.Close SaveChanges:=False文档
  2. 修改 Left(CurrentWB.Name, Len(CurrentWB.Name) - 5) 中的 5 可以使其适用于 .xls 文件。文档 或许我们应该使用正则表达式来移除文件扩展名,但对于一次性脚本来说似乎工作量太大。
- KuN
@KuN:TempWB.close 的更改有什么作用? - neves
我认为这是一个“翻译失误”的问题,如果你查看我提供的文档链接或者@Ralph的回答,你会发现那是调用Workbook.Close的正确方式。 - KuN
1
太好了,我只是添加了一个小东西,即 pastespecial xlPasteFormats,这样我的日期就保持为日期 :DWith TempWB.Sheets(1).Range("A1") .PasteSpecial xlPasteValues .PasteSpecial xlPasteFormats End With - Craig Lambie
很好的提示@CraigLambie,我刚刚将其添加到原始代码中。 - neves
显示剩余3条评论

5
根据我在@neves帖子中的评论,我稍微进行了改进,除了值部分外还添加了xlPasteFormats,这样日期就可以作为日期来传递 - 我主要将其保存为CSV格式用于银行对账单,所以需要日期。
Sub ExportAsCSV()

    Dim MyFileName As String
    Dim CurrentWB As Workbook, TempWB As Workbook

    Set CurrentWB = ActiveWorkbook
    ActiveWorkbook.ActiveSheet.UsedRange.Copy

    Set TempWB = Application.Workbooks.Add(1)
    With TempWB.Sheets(1).Range("A1")
        .PasteSpecial xlPasteValues
        .PasteSpecial xlPasteFormats
    End With

    'Dim Change below to "- 4"  to become compatible with .xls files
    MyFileName = CurrentWB.Path & "\" & Left(CurrentWB.Name, Len(CurrentWB.Name) - 5) & ".csv"

    Application.DisplayAlerts = False
    TempWB.SaveAs Filename:=MyFileName, FileFormat:=xlCSV, CreateBackup:=False, Local:=True
    TempWB.Close SaveChanges:=False
    Application.DisplayAlerts = True
End Sub

希望这可以作为一个插件,有人有时间来实现吗? - Craig Lambie
我必须在Mac上完成这个任务,但我没有Mac来测试。它能工作吗?它是操作系统无关的吗? - Horaciux
1
抱歉@horaciux,我也没有Mac电脑。据我记忆,在旧版的Mac Excel中可用的编码非常有限...可能已经改变了,但说实话我不确定。 - Craig Lambie

3

这里针对上面的答案进行了一些小改进,同时在同一程序中处理 .xlsx 和 .xls 文件,希望能对某些人有所帮助!

我还添加了一行代码,以选择使用活动表格的名称保存而不是工作簿名称,这对我来说很实用:

Sub ExportAsCSV()

    Dim MyFileName As String
    Dim CurrentWB As Workbook, TempWB As Workbook

    Set CurrentWB = ActiveWorkbook
    ActiveWorkbook.ActiveSheet.UsedRange.Copy

    Set TempWB = Application.Workbooks.Add(1)
    With TempWB.Sheets(1).Range("A1")
        .PasteSpecial xlPasteValues
        .PasteSpecial xlPasteFormats
    End With

    MyFileName = CurrentWB.Path & "\" & Left(CurrentWB.Name, InStrRev(CurrentWB.Name, ".") - 1) & ".csv"
    'Optionally, comment previous line and uncomment next one to save as the current sheet name
    'MyFileName = CurrentWB.Path & "\" & CurrentWB.ActiveSheet.Name & ".csv"


    Application.DisplayAlerts = False
    TempWB.SaveAs Filename:=MyFileName, FileFormat:=xlCSV, CreateBackup:=False, Local:=True
    TempWB.Close SaveChanges:=False
    Application.DisplayAlerts = True
End Sub

1

对于那些需要更多输出定制(分隔符或十进制符号)或者拥有大型数据集(超过65k行)的情况,我编写了以下内容:

Option Explicit

Sub rng2csv(rng As Range, fileName As String, Optional sep As String = ";", Optional decimalSign As String)
'export range data to a CSV file, allowing to chose the separator and decimal symbol
'can export using rng number formatting!
'by Patrick Honorez --- www.idevlop.com
    Dim f As Integer, i As Long, c As Long, r
    Dim ar, rowAr, sOut As String
    Dim replaceDecimal As Boolean, oldDec As String

    Dim a As Application:   Set a = Application

    ar = rng
    f = FreeFile()
    Open fileName For Output As #f

    oldDec = Format(0, ".")     'current client's decimal symbol
    replaceDecimal = (decimalSign <> "") And (decimalSign <> oldDec)

    For Each r In rng.Rows
        rowAr = a.Transpose(a.Transpose(r.Value))
        If replaceDecimal Then
            For c = 1 To UBound(rowAr)
                'use isnumber() to avoid cells with numbers formatted as strings
                If a.IsNumber(rowAr(c)) Then
                    'uncomment the next 3 lines to export numbers using source number formatting
'                    If r.cells(1, c).NumberFormat <> "General" Then
'                        rowAr(c) = Format$(rowAr(c), r.cells(1, c).NumberFormat)
'                    End If
                    rowAr(c) = Replace(rowAr(c), oldDec, decimalSign, 1, 1)
                End If
            Next c
        End If
        sOut = Join(rowAr, sep)
        Print #f, sOut
    Next r
    Close #f

End Sub

Sub export()
    Debug.Print Now, "Start export"
    rng2csv shOutput.Range("a1").CurrentRegion, RemoveExt(ThisWorkbook.FullName) & ".csv", ";", "."
    Debug.Print Now, "Export done"
End Sub

谢谢,Patrick。你能解释一下 a.Transpose(a.Transpose(r.Value)) 的作用吗? - Dodecaphone
1
@Dodecaphone,“双重转置”用于将二维数组转换为一维数组。必须拥有一个一维数组才能使“Join”正常工作。 - iDevlop

0
  1. 您可以使用Worksheet.Copy而不带参数将工作表复制到新工作簿中。Worksheet.Move将把工作表复制到新工作簿中并从原始工作簿中删除它(您可以说“导出”它)。
  2. 获取对新创建的工作簿的引用并保存为CSV格式。
  3. 将DisplayAlerts设置为false以抑制警告消息。(别忘了在完成后将其打开)。
  4. 当您保存工作簿和关闭工作簿时,您需要关闭DisplayAlerts。
    wsToExport.Move

    With Workbooks
        Set wbCsv = .Item(.Count)
    End With

    Application.DisplayAlerts = False
    wbCsv.SaveAs xlCSV
    wbCsv.Close False
    Application.DisplayAlerts = True

-1

正如我所评论的,这个网站上有一些地方将工作表的内容写入CSV文件。 这个那个 就是其中之二。

以下是我的版本

  • 它明确查找单元格内的“,”
  • 它还使用 UsedRange - 因为您想获取工作表中的所有内容
  • 使用数组进行循环,因为这比循环工作表单元格更快
  • 我没有使用FSO例程,但这是一个选项

代码...

Sub makeCSV(theSheet As Worksheet)
Dim iFile As Long, myPath As String
Dim myArr() As Variant, outStr As String
Dim iLoop As Long, jLoop As Long

myPath = Application.ActiveWorkbook.Path
iFile = FreeFile
Open myPath & "\myCSV.csv" For Output Lock Write As #iFile

myArr = theSheet.UsedRange
For iLoop = LBound(myArr, 1) To UBound(myArr, 1)
    outStr = ""
    For jLoop = LBound(myArr, 2) To UBound(myArr, 2) - 1
        If InStr(1, myArr(iLoop, jLoop), ",") Then
            outStr = outStr & """" & myArr(iLoop, jLoop) & """" & ","
        Else
            outStr = outStr & myArr(iLoop, jLoop) & ","
        End If
    Next jLoop
    If InStr(1, myArr(iLoop, jLoop), ",") Then
        outStr = outStr & """" & myArr(iLoop, UBound(myArr, 2)) & """"
    Else
        outStr = outStr & myArr(iLoop, UBound(myArr, 2))
    End If
    Print #iFile, outStr
Next iLoop

Close iFile
Erase myArr

End Sub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接