【问题标题】:How to write rows with the file name in Column A to CSV files?如何将 A 列中具有文件名的行写入 CSV 文件?
【发布时间】:2021-09-17 19:06:02
【问题描述】:

我正在尝试从 Excel 中的一组记录生成 CSV 文件。

A 列是文件名,其余列是要写入文件的数据。

截至目前,我正在使用WriteLine,但它没有按预期工作:

如您所见,我没有得到预期的输出。如何获得预期的输出?

Private Sub ommandButton1_Click()
    Dim Path As String
    Dim Folder As String
    Dim Answer As VbMsgBoxResult
    
    Path = "C:\Access Permissions\Users"
    Folder = Dir(Path, vbDirectory)
    
    If Folder = vbNullString Then
    
        '-------------Create Folder -----------------------
        MkDir ("C:\Access Permissions")
        MkDir ("C:\Access Permissions\Roles")
        MkDir ("C:\Access Permissions\Users")
    
    Else
        Set rngSource = Range("A4", Range("A" & Rows.Count).End(xlUp))
        rngSource.Copy Range("AA1")
        Range("AA:AA").RemoveDuplicates Columns:=1, Header:=xlNo
        Set rngUnique = Range("AA1", Range("AA" & Rows.Count).End(xlUp))
        Set lr = Cells(rngSource.Rows.Count, rngSource.Column)
        Set fso = CreateObject("Scripting.FileSystemObject")
        
        For Each cell In rngUnique
            n = Application.CountIf(rngSource, cell.Value)
            Set C = rngSource.Find(cell.Value, lookat:=xlWhole, after:=lr)
            Set oFile = fso.CreateTextFile("C:\Access Permissions\Users\" & cell.Value & "-Users.csv")
            For i = 1 To n
            
                oFile.WriteLine C.Offset(0, 1).Value
                oFile.WriteLine C.Offset(0, 2).Value
                oFile.WriteLine C.Offset(0, 3).Value
                oFile.WriteLine C.Offset(0, 4).Value
                
                oFile.WriteLine C.Offset(0, 6).Value
                oFile.WriteLine C.Offset(0, 7).Value
                 
                Set C = rngSource.FindNext(C)
            Next i
        
        Next cell
        
        rngUnique.ClearContents
        MsgBox "Individual Users.csv files got generated" & vbCrLf & "  " & vbCrLf & "Path - C:\Access Permissions\Groups "
    End If
End Sub

更新图片

让我重新表述我的问题。

附上更新的图片。

  1. 使用数据集[更新图像点 1],它基于 A 列创建唯一的 CSV 文件。

  2. 文件保存在给定的路径。

  3. 到目前为止,与每个文件名关联的行数据已写入文件中,但以换行方式。

  4. 正如所料,如何将输出写入 Columns。[更新图像点 4]

  5. 给定的代码运行没有任何错误。 5.1。如果 Path 文件夹不存在,我只需要单击两次。 5.2.第一次单击时,它会在给定路径创建文件夹。 5.3.在第二次单击时,它会生成唯一的文件及其记录。

如果可以请指导我如何将记录写入列 [ 更新图像点 4 ],预期输出。

Download File

【问题讨论】:

    标签: excel vba


    【解决方案1】:

    我假设您的数据不包含任何分号。

    您将每个字段单独写在一行上。相反,将字段连接在一行中:

    oFile.WriteLine C.Offset(0, 1).Value & ";" & _
                    C.Offset(0, 2).Value & ";" & _
                    C.Offset(0, 3).Value & ";" & _
                    C.Offset(0, 4).Value & ";" & _
                    C.Offset(0, 6).Value & ";" & _
                    C.Offset(0, 7).Value
    

    您的示例中还有其他错误;据我所知,它不应该工作。例如,您不断用CreateTextFile 打开同一个文件。您应该只创建一次文件,而不是每次写入文件。根据文档,您应该在第二次尝试时遇到错误,请参阅CreateTextFile method。您没有收到错误的原因可能是因为您从未关闭文件。您应该关闭您创建的文件。

    我会改用这种方法:

    ' Collect the data for each file into a dictionary.
    ' The cells in the table must not contain semicolons.
    Sub Doit()
        Dim Sht As Worksheet
        Dim Rng As Range
        Dim LastRowNum As Long, LastColNum  As Long
        Dim Lst As Variant, Hdr As Variant, Elem As Variant
        Dim Idx As Long, Idx2 As Long
        Dim Dct As Object
        Dim HdrTxt, Txt As String, Sep As String
        Dim Filename As String
        
        Set Sht = ActiveSheet
        
        ' Get the last row in column 1
        Set Rng = Sht.Cells(Sht.Rows.Count, 1).End(xlUp)
        LastRowNum = Rng.Row
        
        ' Get the last column in row 3
        Set Rng = Sht.Cells(3, Sht.Columns.Count).End(xlToLeft)
        LastColNum = Rng.Column
        
        ' Get the headers in row 3
        Set Rng = Sht.Range(Sht.Cells(3, 1), Sht.Cells(3, LastColNum))
        Hdr = Rng
        
        ' Create a semicolon seprated line for the headers
        HdrTxt = ""
        Sep = ""
        For Idx = LBound(Hdr, 2) To UBound(Hdr, 2)
            HdrTxt = HdrTxt & Sep & Hdr(1, Idx)
            Sep = ";"
        Next Idx
        HdrTxt = HdrTxt & vbNewLine
        
        ' Get the data from row 4 and down
        Set Rng = Sht.Range(Sht.Cells(4, 1), Sht.Cells(LastRowNum, LastColNum))
        Lst = Rng
        
        ' Store the data for each file in a dicitonary
        Set Dct = CreateObject("Scripting.Dictionary")
        For Idx = LBound(Lst) To UBound(Lst)
            Filename = Lst(Idx, 1)
            ' Create a semicolon seprated line
            Txt = ""
            Sep = ""
            For Idx2 = LBound(Lst, 2) To UBound(Lst, 2)
                Txt = Txt & Sep & Lst(Idx, Idx2)
                Sep = ";"
            Next Idx2
            Txt = Txt & vbNewLine
            ' Add the line to the dictionary
            If Dct.Exists(Filename) Then
                Dct(Filename) = Dct(Filename) & Txt
            Else
                Dct(Filename) = HdrTxt & Txt
            End If
        Next Idx
        
        ' Output data for each file to the immdiate window
        For Each Elem In Dct
            ' Change this to open the file and write the contents
            Debug.Print "---- Filename: " & Elem
            Debug.Print Dct(Elem)
        Next Elem
    End Sub
    

    该示例确保您只创建一次文件:

    更改将数据打印到即时窗口的最后一个循环For Each Elem In Dct,改为创建文件。使用 Write method 而不是 WriteLine 方法,因为数据已经包含换行符。记得Close文件。

    【讨论】:

    • 为什么将范围存储在变体中而不是遍历电子表格上的单元格?这有更好的性能吗?
    • 当你将一个Variant分配给一个Range(直接不使用Set),该范围内的数据是转换为数组(前提是该范围包含多个单元格)。限制引用 excel 对象(COM 对象)的次数,在大多数情况下会提高性能。
    【解决方案2】:

    我想这就是你想要的。

    Sub Copy_To_Workbooks()
    'Note: This macro use the function LastRow
        Dim My_Range As Range
        Dim FieldNum As Long
        Dim FileExtStr As String
        Dim FileFormatNum As Long
        Dim CalcMode As Long
        Dim ViewMode As Long
        Dim ws2 As Worksheet
        Dim MyPath As String
        Dim foldername As String
        Dim Lrow As Long
        Dim cell As Range
        Dim CCount As Long
        Dim WSNew As Worksheet
        Dim ErrNum As Long
    
        'Set filter range on ActiveSheet: A1 is the top left cell of your filter range
        'and the header of the first column, D is the last column in the filter range.
        'You can also add the sheet name to the code like this :
        'Worksheets("Sheet1").Range("A1:D" & LastRow(Worksheets("Sheet1")))
        'No need that the sheet is active then when you run the macro when you use this.
        Set My_Range = Range("A1:B" & LastRow(ActiveSheet))
        My_Range.Parent.Select
    
        If ActiveWorkbook.ProtectStructure = True Or _
           My_Range.Parent.ProtectContents = True Then
            MsgBox "Sorry, not working when the workbook or worksheet is protected", _
                   vbOKOnly, "Copy to new workbook"
            Exit Sub
        End If
    
        'This example filters on the first column in the range(change the field if needed)
        'In this case the range starts in A so Field:=1 is column A, 2 = column B, ......
        FieldNum = 1
    
        'Turn off AutoFilter
        My_Range.Parent.AutoFilterMode = False
    
        'Set the file extension/format
        If Val(Application.Version) < 12 Then
            'You use Excel 97-2003
            FileExtStr = ".txt": FileFormatNum = -4143
        Else
            'You use Excel 2007-2010
            If ActiveWorkbook.FileFormat = 56 Then
                FileExtStr = ".txt": FileFormatNum = 56
            Else
                FileExtStr = ".txt": FileFormatNum = 51
            End If
        End If
    
        'Change ScreenUpdating, Calculation, EnableEvents, ....
        With Application
            CalcMode = .Calculation
            .Calculation = xlCalculationManual
            .ScreenUpdating = False
            .EnableEvents = False
        End With
        ViewMode = ActiveWindow.View
        ActiveWindow.View = xlNormalView
        ActiveSheet.DisplayPageBreaks = False
    
        'Delete the sheet RDBLogSheet if it exists
        On Error Resume Next
        Application.DisplayAlerts = False
        Sheets("RDBLogSheet").Delete
        Application.DisplayAlerts = True
        On Error GoTo 0
    
        ' Add worksheet to copy/Paste the unique list
        Set ws2 = Worksheets.Add(After:=Sheets(Sheets.Count))
        ws2.Name = "RDBLogSheet"
    
        'Fill in the path\folder where you want the new folder with the files
        'you can use also this "C:\Users\Ron\test"
        MyPath = "C:\Users\ryans\OneDrive\Desktop\"
    
        'Add a slash at the end if the user forget it
        If Right(MyPath, 1) <> "\" Then
            MyPath = MyPath & "\"
        End If
    
        'Create folder for the new files
        foldername = MyPath & Format(Now, "yyyy-mm-dd hh-mm-ss") & "\"
        MkDir foldername
    
        With ws2
            'first we copy the Unique data from the filter field to ws2
            My_Range.Columns(FieldNum).AdvancedFilter _
                    Action:=xlFilterCopy, _
                    CopyToRange:=.Range("A3"), Unique:=True
    
            'loop through the unique list in ws2 and filter/copy to a new sheet
            Lrow = .Cells(Rows.Count, "A").End(xlUp).Row
            For Each cell In .Range("A4:A" & Lrow)
    
                'Filter the range
                My_Range.AutoFilter Field:=FieldNum, Criteria1:="=" & _
                 Replace(Replace(Replace(cell.Value, "~", "~~"), "*", "~*"), "?", "~?")
    
                'Check if there are no more then 8192 areas(limit of areas)
                CCount = 0
                On Error Resume Next
                CCount = My_Range.Columns(1).SpecialCells(xlCellTypeVisible) _
                         .Areas(1).Cells.Count
                On Error GoTo 0
                If CCount = 0 Then
                    MsgBox "There are more than 8192 areas for the value : " & cell.Value _
                         & vbNewLine & "It is not possible to copy the visible data." _
                         & vbNewLine & "Tip: Sort your data before you use this macro.", _
                           vbOKOnly, "Split in worksheets"
                Else
                    'Add new workbook with one sheet
                    Set WSNew = Workbooks.Add(xlWBATWorksheet).Worksheets(1)
    
                    'Copy/paste the visible data to the new workbook
                    My_Range.SpecialCells(xlCellTypeVisible).Copy
                    With WSNew.Range("A1")
                        ' Paste:=8 will copy the columnwidth in Excel 2000 and higher
                        ' Remove this line if you use Excel 97
                        .PasteSpecial Paste:=8
                        .PasteSpecial xlPasteValues
                        .PasteSpecial xlPasteFormats
                        Application.CutCopyMode = False
                        .Select
                    End With
    
                    'Save the file in the new folder and close it
                    On Error Resume Next
                    ChDir MyPath
                    ActiveWorkbook.SaveAs Filename:= _
                    foldername & cell.Value & ".txt", _
                    FileFormat:=xlTextMSDOS, CreateBackup:=False
    
            
                    If Err.Number > 0 Then
                        Err.Clear
                        ErrNum = ErrNum + 1
    
                        WSNew.Parent.SaveAs foldername & _
                         "Error_" & Format(ErrNum, "0000") & FileExtStr, FileFormatNum
    
                        .Cells(cell.Row, "B").Formula = "=Hyperlink(""" & foldername & _
                          "Error_" & Format(ErrNum, "0000") & FileExtStr & """)"
    
                        .Cells(cell.Row, "A").Interior.Color = vbRed
                    Else
                        .Cells(cell.Row, "B").Formula = _
                        "=Hyperlink(""" & foldername & cell.Value & FileExtStr & """)"
                    End If
    
                    WSNew.Parent.Close False
                    On Error GoTo 0
                End If
    
                'Show all the data in the range
                My_Range.AutoFilter Field:=FieldNum
    
            Next cell
            .Cells(1, "A").Value = "Red cell: can't use the Unique name as file name"
            .Cells(1, "B").Value = "Created Files (Click on the link to open a file)"
            .Cells(3, "A").Value = "Unique Values"
            .Cells(3, "B").Value = "Full Path and File name"
            .Cells(3, "A").Font.Bold = True
            .Cells(3, "B").Font.Bold = True
            .Columns("A:B").AutoFit
    
        End With
    
        'Turn off AutoFilter
        My_Range.Parent.AutoFilterMode = False
    
        If ErrNum > 0 Then
            MsgBox "Rename every WorkSheet name that start with ""Error_"" manually" _
                 & vbNewLine & "There are characters in the name that are not allowed" _
                 & vbNewLine & "in a sheet name or the worksheet already exist."
        End If
    
        'Restore ScreenUpdating, Calculation, EnableEvents, ....
        My_Range.Parent.Select
        ActiveWindow.View = ViewMode
        ws2.Select
        With Application
            .ScreenUpdating = True
            .EnableEvents = True
            .Calculation = CalcMode
        End With
    
    End Sub
    
    
    Function LastRow(sh As Worksheet)
        On Error Resume Next
        LastRow = sh.Cells.Find(What:="*", _
                                After:=sh.Range("A1"), _
                                Lookat:=xlPart, _
                                LookIn:=xlValues, _
                                SearchOrder:=xlByRows, _
                                SearchDirection:=xlPrevious, _
                                MatchCase:=False).Row
        On Error GoTo 0
    End Function
    

    之前:

    之后:

    【讨论】:

    • 感谢专家的解决方案和建议,我提供了指向我的文件的链接并更新了图像以获得预期的输出。该文件没有任何错误。谢谢
    • 那么,我的建议对你有用吗?我有点困惑。
    • 谢谢先生,您的解决方案适用于在时间戳、文件夹和登录主数据表下生成的错误文件。所有的错误文件都有一些 ASCII 码。
    • 我仍然不知道你在说什么,但听起来你离你想去的地方非常非常近。我会让你算出最后的1%。这对你来说将是一个很好的学习练习。
    • 首先我对 vba 很陌生,我试过你的代码,但它只选择了两列,而且是一个 txt 文件。它必须是 CVS 文件。根据屏幕截图,数据集由多列组成,但在创建 CVS 文件时,它仅使用数据集中的几列。因此,它不应包含角色列作为生成的文件。
    猜你喜欢
    • 2017-05-17
    • 2020-10-14
    • 1970-01-01
    • 2019-03-08
    • 1970-01-01
    • 1970-01-01
    • 2019-09-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多