简体   繁体   English

使用VBA从文本文件写入excel时保留“列”

[英]Preserving "columns" when writing from a text file to excel using VBA

I have a text file that is formatted in the following way:我有一个格式如下的文本文件:

在此处输入图片说明

And I am using the below code in VBA to write the text file into excel:我在 VBA 中使用以下代码将文本文件写入 excel:

Sub Test()

 Dim Fn As String, WS As Worksheet, st As String

 Fn = "Path.txt" ' the file path and name
 Set WS = Sheets("Sheet1")

 'Read text file to st string
 With CreateObject("Scripting.FileSystemObject")
    If Not .FileExists(Fn) Then
        MsgBox Fn & "  : is missing."
        Exit Sub
    Else
        If FileLen(Fn) = 0 Then
            MsgBox Fn & "  : is empty"
            Exit Sub
        Else
            With .OpenTextFile(Fn, 1)
             st = .ReadAll
             .Close
            End With
        End If
    End If
 End With

 'Replace every two or more space in st string with vbTab
 With CreateObject("VBScript.RegExp")
  .Pattern = "[ ]{2,}"
  .Global = True
  .Execute st
  st = .Replace(st, vbTab)
 End With

 'Put st string in Clipboard
 With CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
    .SetText st
    .PutInClipboard
 End With

 'Paste Clipboard to range
 WS.Range("A1").PasteSpecial

End Sub

My goal is to preserve the columns from the text file in Excel.我的目标是在 Excel 中保留文本文件中的列。

However, my code can't tell that a blank space under Plan Type and a blank space under Benefit Plan are actually two different columns of data.但是,我的代码无法分辨Plan Type下的空白区域和Benefit Plan下的空白区域实际上是两列不同的数据。 It treats the blank space under the two columns as one long blank space, and the formatting isn't preserved.它将两列下的空格视为一个长空格,并且不保留格式。

Visually we know there are columns, but my code cannot see this.视觉上我们知道有列,但我的代码看不到这一点。

Is there a way to program this so it recognizes that there are two spaces in the text file instead of one big space?有没有办法对此进行编程,以便识别文本文件中有两个空格而不是一个大空格?

What I want to avoid is having to manually deliminate this with a character.我想避免的是必须用字符手动分隔它。 Is that possible?那可能吗?

Assuming that each column is 10 characters long, I would use this width instead of a space delimeter假设每列的长度为10字符,我将使用此宽度而不是空格分隔符

Sub FeedTextFileToActiveSheet(ByVal TextFile As String)
  Dim i As Integer, Line As String
  Open TextFile For Input As #1
  While Not EOF(#1)
    i = i + 1
    Input #1, Line
    Range("A" & i) = Trim(Mid(Line, 1, 10))  'Business ID
    Range("B" & i) = Trim(Mid(Line, 11, 10)) 'Employee ID
    ' ... and so on
  Wend
  Close #1
End Sub

To use it, just call FeedTextFileToActiveSheet("Path.txt")要使用它,只需调用FeedTextFileToActiveSheet("Path.txt")

Have you tried the "import from text file option" of excel?您是否尝试过excel的“从文本文件导入选项”? If you just want to import the text file to excel with or without headers, then you can import directly in excel using the built in option available in excel .This recognises the header and blank spaces properly.One point to be noted is the headers of the text file should always be in first line for this method.如果您只想将文本文件导入有或没有标题的 excel,那么您可以使用 excel 中可用的内置选项直接在 excel 中导入。这可以正确识别标题和空格。需要注意的一点是对于此方法,文本文件应始终位于第一行。 If you are not sure of this, then you can go for a vba script.if so, then the link provided by ferdinando will help you.如果您不确定这一点,那么您可以使用 vba 脚本。如果是这样,那么 ferdinando 提供的链接将对您有所帮助。

If you have this file organized visually, I would go by that logic.如果您以视觉方式组织了这个文件,我会按照这种逻辑进行。 It means that value of a column starts where the column header starts.这意味着列的值从列标题开始的地方开始。 This implies that value of a column ends where the next one begins.这意味着一列的值在下一列开始的地方结束。

Helpful image, describing the logic (also, example text file I used):有用的图片,描述了逻辑(还有我使用的示例文本文件):

在此处输入图片说明

All this logic can be done by reading first line, which contains headers, and determining all indexes of beginning of every header.所有这些逻辑都可以通过读取包含标题的第一行并确定每个标题开头的所有索引来完成。 Then, for each line we can easily determine value between two particular indexes, cut it out and trim to remove extra spaces at the beginning and at the end of a value.然后,对于每一行,我们可以轻松确定两个特定索引之间的值,将其剪掉并修剪以删除值开头和结尾的多余空格。

Try below code (all necessary comments in code):试试下面的代码(代码中所有必要的注释):

Sub ReadDataFromCsv()
    Dim Fn As String, WS As Worksheet, st As String, i As Long, columnHeadersIndexes As Object, numberOfColumns As Long
    Fn = "your path here" ' the file path and name
    Set WS = Sheets("Sheet1")
    ' Create array that will hold indexes of a beginning of a column header
    Set columnHeadersIndexes = CreateObject("System.Collections.ArrayList")
    'Read text file to st string
    With CreateObject("Scripting.FileSystemObject")
        If Not .FileExists(Fn) Then
            MsgBox Fn & "  : is missing."
            Exit Sub
        ElseIf FileLen(Fn) = 0 Then
            MsgBox Fn & "  : is empty"
        Else
            With .OpenTextFile(Fn, 1)
                ' Read first line
                st = .ReadLine
                i = 1
                ' Find beginning of first column name
                Do While Mid(st, i, 1) = " "
                    i = i + 1
                Loop
                columnHeadersIndexes.Add (i)
                ' At least two spaces separate two headers, so we can safely add 2 without risk of loosing any letters frmo next header
                i = i + 2
                Dim j As Long: j = 1
                Do While i < Len(st)
                    ' If we have two spaces followed by non-space, then save index (beginning of a header)
                    If Mid(st, i - 2, 2) = "  " And Mid(st, i, 1) <> " " Then
                        ' Set column header
                        Cells(1, j) = Mid(st, columnHeadersIndexes(columnHeadersIndexes.Count - 1), i - columnHeadersIndexes(columnHeadersIndexes.Count - 1) - 1)
                        columnHeadersIndexes.Add (i)
                        j = j + 1
                    End If
                    i = i + 1
                Loop
                ' Set column header
                Cells(1, j) = Trim(Mid(st, columnHeadersIndexes(columnHeadersIndexes.Count - 1), Len(st)))
                numberOfColumns = columnHeadersIndexes.Count
                ' Skip line with ------ characters
                .ReadLine
                Dim currentRow As Long: currentRow = 2
                Do While .AtEndOfStream <> True
                    st = .ReadLine
                    ' Read all columns from a line
                    For i = 0 To numberOfColumns - 2
                        If Len(st) >= columnHeadersIndexes(i) Then
                            cellValue = Mid(st, columnHeadersIndexes(i), columnHeadersIndexes(i + 1) - columnHeadersIndexes(i) - 1)
                            cellValue = Trim(cellValue)
                            Cells(currentRow, i + 1) = cellValue
                        End If
                    Next
                    ' Read last column, if exists
                    If Len(st) >= columnHeadersIndexes(i) Then
                        'here we pass Len(st) as length for substring - it assures that we don't pass too small value and miss some characters
                        cellValue = Mid(st, columnHeadersIndexes(i), Len(st))
                        cellValue = Trim(cellValue)
                        Cells(currentRow, i + 1) = cellValue
                    End If
                    currentRow = currentRow + 1
                Loop
                .Close
            End With
        End If
    End With
End Sub

If the file looks exactly alike the image when opened in notepad, most probably it is fixed width .如果文件在记事本中打开时看起来与图像完全相同,则很可能是固定宽度 Whatever may be the case better go a blank workbook, start Record Macro and simply try to open the text file.无论情况如何,最好使用空白工作簿,启动录制宏并尝试打开文本文件。 Automatically Text import wizard will open.自动文本导入向导将打开。 Chose type as Fixed Width (preferably) or delimited, go through each step carefully reading the guiding instruction provided.选择固定宽度(最好)或定界类型,仔细阅读提供的指导说明,完成每一步。 (When asked for start import at row, it is better to give first line containing significant data, omitting header lines etc). (当要求在行开始导入时,最好给出包含重要数据的第一行,省略标题行等)。 When the file is fully opened stop the recording.当文件完全打开时停止录制。 You will have a recorded macro something like this.您将拥有一个类似这样的录制宏。

Workbooks.OpenText Filename:="C:\Users\user\Desktop\Text.prn", Origin:= _
        xlMSDOS, StartRow:=5, DataType:=xlFixedWidth, FieldInfo:=Array(Array(0, 1) _
        , Array(14, 1), Array(29, 1), Array(44, 1), Array(59, 1), Array(74, 5), Array(89, 1), Array( _
        104, 1)), TrailingMinusNumbers:=True

Now simply use that portion of code (may be with little modification in filename etc) in your procedure to Open the text file.现在只需在您的过程中使用该部分代码(可能在文件名等方面稍作修改)来打开文本文件。 Then simply copy the current region and paste in a work sheet already made ready with headers etc like.然后只需复制当前区域并粘贴到已经准备好标题等的工作表中。

ActiveWorkbook.ActiveSheet.Range("A1").CurrentRegion.Copy ThisWorkbook.Sheets(1).Range("a5")
 ActiveWorkbook.Close False

you could:你可以:

  • process the line with all "-"s, to get the actual fields width处理带有所有“-”的行,以获得实际的字段宽度

  • paste all text content into wanted sheet column A rows将所有文本内容粘贴到所需的工作表列 A 行中

  • use TextToColumns() method to spread text from column A into as many columns as needed, determined by proper handling of the "-"s line使用TextToColumns()方法根据需要将 A 列中的文本扩展到尽可能多的列中,这取决于正确处理“-”行

as follows:如下:

Option Explicit

Sub Test()

    Dim Fn As String, WS As Worksheet
    Dim lines As Variant, line As Variant

    Fn = "Path.txt" ' the file path and name
    Set WS = Sheets("Sheet1")

    'Read text file to st string
    With CreateObject("Scripting.FileSystemObject")
       If Not .FileExists(Fn) Then
           MsgBox Fn & "  : is missing."
           Exit Sub
       Else
           If FileLen(Fn) = 0 Then
               MsgBox Fn & "  : is empty"
               Exit Sub
           Else
                With .OpenTextFile(Fn, 1)
                    lines = Split(.readall, vbLf)
                    .Close
                End With
           End If
       End If
    End With

    For Each line In lines ' loop through all text lines
        If InStr(line, "-") > 0 Then Exit For ' loop till you reach the "-"s line, which will be used to get FieldInfo array for textToColumns method
    Next

    With WS
        .Range("a1").Resize(UBound(lines) + 1).Value = Application.Transpose(lines) ' copy all text lines into column A rows
        .Columns(1).TextToColumns Destination:=Range("A1"), DataType:=xlFixedWidth, FieldInfo:=GetFieldInfo(Trim(line)), TrailingMinusNumbers:=True ' convert text to columns feeding FieldInfo array arranged from the "-"s line structure
    End With

End Sub


Function GetFieldInfo(st As String) As Variant()
    Dim i As Long, n As Long, nFields As Long

    nFields = UBound(Split(WorksheetFunction.Trim(st), " ")) ' get the number of fields by counting the "-"s groups separated by single space

    ReDim arrtext(0 To nFields) ' size FieldInfo array accordingly
    Do
        arrtext(i) = Array(n, 1) ' build current FieldInfo array field with current field position in text
        n = InStr(n + 1, st, " -") ' search next field position
        i = i + 1
    Loop While i < nFields
    arrtext(i) = Array(n, 1) ' build last FieldInfo array field with last field position in text

    GetFieldInfo = arrtext ' return FieldInfo array
End Function

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM