【问题标题】:Export Excel range/sheet to formatted text file将 Excel 范围/工作表导出到格式化的文本文件
【发布时间】:2010-10-03 04:23:39
【问题描述】:

我的任务是为我们的财务部门创建一个可重复使用的流程,以将我们的工资单上传到州 (WI) 进行报告。我需要在 Excel 中创建一个工作表或范围并创建一个特定格式的文本文件的东西。

格式

  • 第 1 列 - 一个静态数字,永不更改,位置 1-10
  • 第 2 列 - 运行时为季度/年度填充的动态参数,位置 11-13
  • 第 3 列 - SSN,没有连字符或空格, 从 A 列第 14-22 位填充
  • 第 4 列 - 姓氏,填写自 B 列,在 10 处截断,左 对齐和填充空白,位置 23-32
  • 第 5 列 - 名字,从 C 填写, 在 8 处截断,左对齐和填充 带空白,位置 33-40
  • 第 6 栏 - 总工资/季度, 从 D 填充,去除所有格式, 右对齐零填充,位置 41-49
  • 第 7 列 - 静态代码,从不 变化,位置 50-51
  • 第 8 列 - 空白,填空, 位置 52-80

我假设有 3 个选项:

  1. VBA
  2. .NET
  3. SQL

我首先探索了 .NET 方法,但我只是找不到合适的文档来帮助我前进。我仍然喜欢这个,但我离题了。

接下来我有一些 VBA 可以将工作表转储为固定宽度的文本。我目前正在追求这个,这最终导致了我的实际问题。

如何在 Excel 中转换文本范围?我是否需要将其复制到另一张纸上,然后使用必要的格式化功能传递该数据以运行我的转储到文本例程?我目前计划为每一列设置一个函数,但我无法弄清楚如何采取下一步行动。我在 Office 编程和一般开发方面相当新,因此任何见解都将不胜感激。

SQL 选项将是我的后备方案,因为我过去曾从 SQL 中进行过类似的导出。我只是更喜欢其他两个,“我不想负责运行这个”,原则。

提前感谢您的任何时间。

【问题讨论】:

  • 优秀的问答。

标签: .net sql vba excel


【解决方案1】:

使用 VBA 似乎适合我。这让您可以编写一个处理所有各种格式选项的宏,并且希望它足够简单,让您的财务人员自己运行。

您说您需要在 Excel 中使用工作表或范围的东西。第一列永远不会改变,因此我们可以将其存储在宏中,第 3-7 列来自电子表格,第 8 列只是空白。这使得第 2 列(季度/年为 QYY)成为一个问题。如果在工作簿中的某处指定了季度/年份(例如,存储在单元格中,作为工作表名称,作为工作簿标题的一部分),那么我们可以将其读入。否则您将需要找到一些指定季度的方法/年宏运行时(例如弹出一个对话框并要求用户输入)

一些简单的代码(我们稍后会担心如何调用它):

Sub ProduceStatePayrollReportFile(rngPayrollData As Range, strCompanyNo As String, _
    strQuarterYear As String, strRecordCode As String, strOutputFile As String)

参数相当明显:保存数据的范围、第 1 列的公司编号、第 2 列的季度/年、第 7 列的固定代码以及我们要将结果输出到的文件

' Store the file handle for the output file
Dim fnOutPayrollReport As Integer
' Store each line of the output file
Dim strPayrollReportLine As String
' Use to work through each row in the range
Dim indexRow As Integer

要输出到 VBA 中的文件,我们需要获取文件句柄,因此我们需要一个变量来存储它。我们将在报告行字符串中构建报告的每一行并使用行索引来处理范围

' Store the raw SSN, last name, first name and wages data
Dim strRawSSN As String
Dim strRawLastName As String
Dim strRawFirstName As String
Dim strRawWages As String
Dim currencyRawWages As Currency

' Store the corrected SSN, last name, first name and wages data
Dim strCleanSSN As String
Dim strCleanLastName As String
Dim strCleanFirstName As String
Dim strCleanWages As String

这些变量集分别存储工作表中的原始数据和要输出到文件的清理数据。将它们命名为“原始”和“干净”可以更容易地发现您意外输出原始数据而不是清理数据的错误。我们需要将原始工资从字符串值更改为数值以帮助格式化

' Open up the output file
fnOutPayrollReport = FreeFile()
Open strOutputFile For Output As #fnOutPayrollReport

FreeFile() 获取下一个可用的文件句柄,我们使用它链接到文件

' Work through each row in the range
For indexRow = 1 To rngPayrollData.Rows.Count
    ' Reset the output report line to be empty
    strPayrollReportLine = ""
    ' Add the company number to the report line (assumption: already correctly formatted)
    strPayrollReportLine = strPayrollReportLine & strCompanyNo
    ' Add in the quarter/year (assumption: already correctly formatted)
    strPayrollReportLine = strPayrollReportLine & strQuarterYear

在遍历每一行的循环中,我们首先清除输出字符串,然后添加第 1 列和第 2 列的值

' Get the raw SSN data, clean it and append to the report line
strRawSSN = rngPayrollData.Cells(indexRow, 1)
strCleanSSN = cleanFromRawSSN(strRawSSN)
strPayrollReportLine = strPayrollReportLine & strCleanSSN

.Cells(indexRow, 1) 部分仅表示由 indexRow 指定的行中范围的最左侧列。如果范围从 A 列开始(不一定是这种情况),那么这只是意味着 A。我们稍后需要自己编写 cleanFromRawSSN 函数

' Get the raw last and first names, clean them and append them
strRawLastName = rngPayrollData.Cells(indexRow, 2)
strCleanLastName = Format(Left$(strRawLastName, 10), "!@@@@@@@@@@")
strPayrollReportLine = strPayrollReportLine & strCleanLastName

strRawFirstName = rngPayrollData.Cells(indexRow, 3)
strCleanFirstName = Format(Left$(strRawFirstName, 8), "!@@@@@@@@")
strPayrollReportLine = strPayrollReportLine & strCleanFirstName

Left$(string, length) 将字符串截断为给定长度。格式图片!@@@@@@@@@@ 将字符串格式化为正好十个字符长,左对齐(! 表示左对齐)并用空格填充

' Read in the wages data, convert to numeric data, lose the decimal, clean it and append it
strRawWages = rngPayrollData.Cells(indexRow, 4)
currencyRawWages = CCur(strRawWages)
currencyRawWages = currencyRawWages * 100
strCleanWages = Format(currencyRawWages, "000000000")
strPayrollReportLine = strPayrollReportLine & strCleanWages

我们将其转换为货币,以便乘以 100 将美分值移到小数点左侧。这使得使用Format 生成正确的值变得更加容易。对于 >= 1000 万美元的工资,这不会产生正确的输出,但这是用于报告的文件格式的限制。 0 格式画板中的 0 就够惊人了

' Append the fixed code for column 7 and the spaces for column 8
strPayrollReportLine = strPayrollReportLine & strRecordCode
strPayrollReportLine = strPayrollReportLine & CStr(String(29, " "))

' Output the line to the file
Print #fnOutPayrollReport, strPayrollReportLine

String(number, char) 函数产生一个变体,其中包含指定charnumber 序列。 CStr 将 Variant 转换为字符串。 Print # 语句输出到文件,没有任何额外的格式

Next indexRow

' Close the file
Close #fnOutPayrollReport

End Sub

循环到范围内的下一行并重复。当我们处理完所有行后,关闭文件并结束宏

我们仍然需要两件事:一个 cleanFromRawSSN 函数和一个使用相关数据调用宏的方法。

Function cleanFromRawSSN(strRawSSN As String) As String

' Used to index the raw SSN so we can process it one character at a time
Dim indexRawChar As Integer

' Set the return string to be empty
cleanFromRawSSN = ""

' Loop through the raw data and extract the correct characters
For indexRawChar = 1 To Len(strRawSSN)
    ' Check for hyphen
    If (Mid$(strRawSSN, indexRawChar, 1) = "-") Then
        ' do nothing
    ' Check for space
    ElseIf (Mid$(strRawSSN, indexRawChar, 1) = " ") Then
        ' do nothing
    Else
        ' Output character
        cleanFromRawSSN = cleanFromRawSSN & Mid$(strRawSSN, indexRawChar, 1)
    End If
Next indexRawChar

' Check for correct length and return empty string if incorrect
If (Len(cleanFromRawSSN) <> 9) Then
    cleanFromRawSSN = ""
End If

End Function

Len 返回一个字符串的长度,Mid$(string, start, length)string 返回从start 开始的length 字符。此功能可以改进,因为它目前不检查非数字数据

调用宏:

Sub CallPayrollReport()

ProduceStatePayrollReportFile Application.Selection, "1234560007", "109", "01", "C:\payroll109.txt"

End Sub

这是最简单的调用方式。范围是用户在活动工作簿的活动工作表上选择的任何内容,其他值是硬编码的。用户应该选择他们想要输出到文件的范围,然后转到工具>宏>运行并选择CallPayrollReport。为此,宏需要成为包含数据的工作簿的一部分,或者位于用户调用宏之前已加载的不同工作簿中。

在生成每个季度的报告之前,有人需要更改季度/年度的硬编码值。如前所述,如果季度/年度已经存储在工作簿中的某个地方,那么最好将其读入而不是硬编码

希望这是有道理且有用的

【讨论】:

    【解决方案2】:

    从对您来说最简单的角度严格考虑这一点,如果您对 SQL 熟悉,在 Access 的上下文中,您可以使用 Access 作为外部数据源附加到电子表格。它看起来像 Access 中的一个表,并从那里开始工作。

    【讨论】:

      【解决方案3】:

      哇!

      我不得不说,我被吹走了。到目前为止,您超出了我对答案的期望,我感到内疚,我只能投票给您一次并标记为例外。我希望得到这样的指导,了解哪条路径是最好的和一些格式。祝我生日快乐!

      Format() 和 FreeFile() 是特别有用的新信息。另外,为了表明我正在尝试,我的尝试如下。我非常接近,因为我刚刚制定了格式细节,但我相信我会根据您的输入重新设计它,因为它似乎是更优雅的方法。

      作为最后一点。我通过 Jeff Atwood 的博客找到了这个地方,我对这个想法感到非常兴奋。作为一个新的、缺乏经验的开发人员,我一直希望有一个可以寻求指导的地方。书籍和文章能让你达到一定的目的,但没有什么能比得上做过或去过那里的人的建议。到目前为止,StackOverflow 已经交付。

      作为参考,我在另一个非常受欢迎的代码论坛上发布了这个完全相同的问题,但还没有收到任何回复。

      现在我的尝试:

      模块代码

      
          Sub StateANSIIExport()
          Dim Sizes As Variant
          Dim arr As Variant
          Dim aRow As Long, aCol As Long
          Dim rowLimit As Integer, colLimit As Integer
          Dim SpacesPerCell As Integer
          Dim fso As Object
          Dim ts As Object
          Dim TheLine As String
          Dim TestStr As String
      
          arr = ActiveSheet.UsedRange
          rowLimit = UBound(arr, 1)
          'colLimit = UBound(arr, 2)
          colLimit = 8
          SpacesPerCell = 20      'Set export text "column" width here
      
          Set fso = CreateObject("Scripting.FileSystemObject")
          Set ts = fso.CreateTextFile(GetDesktopPath() & "EXCELTEXT.txt", True)
      
          ' Loop thru the rows
          For aRow = 1 To rowLimit
              TheLine = Space(colLimit * SpacesPerCell)     ' your fixed-width output
              ' Loop thru the columns
              For aCol = 1 To colLimit
                  Select Case aCol
                      Case 1  ' Employer UI Account #
                          Mid(TheLine, aCol * SpacesPerCell - SpacesPerCell + 1, SpacesPerCell) = "6979430002"
                      Case 2  ' Reporting Period (QYY)
                          Mid(TheLine, aCol * SpacesPerCell - SpacesPerCell + 1, SpacesPerCell) = "109"
                      Case 3  ' SSN
                          Mid(TheLine, aCol * SpacesPerCell - SpacesPerCell + 1, SpacesPerCell) = Cells(aRow, "A")
                      Case 4  ' Last Name
                          Mid(TheLine, aCol * SpacesPerCell - SpacesPerCell + 1, SpacesPerCell) = Cells(aRow, "B")
                      Case 5  ' First Name
                          Mid(TheLine, aCol * SpacesPerCell - SpacesPerCell + 1, SpacesPerCell) = Cells(aRow, "C")
                      Case 6  ' Employee Quartly Gross Wages
                          Mid(TheLine, aCol * SpacesPerCell - SpacesPerCell + 1, SpacesPerCell) = Cells(aRow, "D")
                      Case 7   ' Record Code
                          Mid(TheLine, aCol * SpacesPerCell - SpacesPerCell + 1, SpacesPerCell) = "01"
                      Case 8  ' BLANK
                          Mid(TheLine, aCol * SpacesPerCell - SpacesPerCell + 1, SpacesPerCell) = "                             "
                  End Select
              Next aCol
              ' Write the line to the file
              ts.WriteLine TheLine
          Next aRow
      
          ts.Close
      
          Set ts = Nothing
          Set fso = Nothing
      
          MsgBox "Done"
      End Sub
      
          Sub MacroToRunTwo()
          Dim S As String
          S = "Hello World From Two:" & vbCrLf & _
              "This Add-In File Name: " & ThisWorkbook.FullName
          MsgBox S
      End Sub
      
      Function GetDesktopPath() As String
      'Return the current user's desktop path
      GetDesktopPath = "C:\Users\patrick\Desktop\"
      'GetDesktopPath = Environ("HOMEDRIVE") & Environ("HOMEPATH") & "\Desktop\"
      End Function
      

      还有工作簿代码:

      
          Private Const C_TAG = "Refracted Solutions" ' C_TAG should be a string unique to this add-in.
      Private Const C_TOOLS_MENU_ID As Long = 30007&
      
      Private Sub Workbook_Open()
      '''''''''''''''''''''''''''''''''''''''''''''''
      ' Workbook_Open
      ' Create a submenu on the Tools menu. The
      ' submenu has two controls on it.
      '''''''''''''''''''''''''''''''''''''''''''''''
      Dim ToolsMenu As Office.CommandBarControl
      Dim ToolsMenuItem As Office.CommandBarControl
      Dim ToolsMenuControl As Office.CommandBarControl
      
      '''''''''''''''''''''''''''''''''''''''''''''''
      ' First delete any of our controls that
      ' may not have been properly deleted previously.
      '''''''''''''''''''''''''''''''''''''''''''''''
      DeleteControls
      
      ''''''''''''''''''''''''''''''''''''''''''''''
      ' Get a reference to the Tools menu.
      ''''''''''''''''''''''''''''''''''''''''''''''
      Set ToolsMenu = Application.CommandBars.FindControl(ID:=C_TOOLS_MENU_ID)
      If ToolsMenu Is Nothing Then
          MsgBox "Unable to access Tools menu.", vbOKOnly
          Exit Sub
      End If
      
      ''''''''''''''''''''''''''''''''''''''''''''''
      ' Create a item on the Tools menu.
      ''''''''''''''''''''''''''''''''''''''''''''''
      Set ToolsMenuItem = ToolsMenu.Controls.Add(Type:=msoControlPopup, temporary:=True)
      If ToolsMenuItem Is Nothing Then
          MsgBox "Unable to add item to the Tools menu.", vbOKOnly
          Exit Sub
      End If
      
      With ToolsMenuItem
          .Caption = "&WWCares"
          .BeginGroup = True
          .Tag = C_TAG
      End With
      
      ''''''''''''''''''''''''''''''''''''''''''''''
      ' Create the first control on the new item
      ' in the Tools menu.
      ''''''''''''''''''''''''''''''''''''''''''''''
      Set ToolsMenuControl = ToolsMenuItem.Controls.Add(Type:=msoControlButton, temporary:=True)
      If ToolsMenuControl Is Nothing Then
          MsgBox "Unable to add item to Tools menu item.", vbOKOnly
          Exit Sub
      End If
      
      With ToolsMenuControl
          ''''''''''''''''''''''''''''''''''''
          ' Set the display caption and the
          ' procedure to run when clicked.
          ''''''''''''''''''''''''''''''''''''
          .Caption = "State ANSII E&xport"
          .OnAction = "'" & ThisWorkbook.Name & "'!StateANSIIExport"
          .Tag = C_TAG
      End With
      
      ''''''''''''''''''''''''''''''''''''''''''''''
      ' Create the second control on the new item
      ' in the Tools menu.
      ''''''''''''''''''''''''''''''''''''''''''''''
      'Set ToolsMenuControl = ToolsMenuItem.Controls.Add(Type:=msoControlButton, temporary:=True)
      'If ToolsMenuControl Is Nothing Then
      '    MsgBox "Unable to add item to Tools menu item.", vbOKOnly
      '    Exit Sub
      'End If
      
      'With ToolsMenuControl
          ''''''''''''''''''''''''''''''''''''
          ' Set the display caption and the
          ' procedure to run when clicked.
          ''''''''''''''''''''''''''''''''''''
      '    .Caption = "Click Me &Two"
      '    .OnAction = "'" & ThisWorkbook.Name & "'!MacroToRunTwo"
      '    .Tag = C_TAG
      'End With
      
      End Sub
      
      
      Private Sub Workbook_BeforeClose(Cancel As Boolean)
      ''''''''''''''''''''''''''''''''''''''''''''''''''''
      ' Workbook_BeforeClose
      ' Before closing the add-in, clean up our controls.
      ''''''''''''''''''''''''''''''''''''''''''''''''''''
          DeleteControls
      End Sub
      
      
      Private Sub DeleteControls()
      ''''''''''''''''''''''''''''''''''''
      ' Delete controls whose Tag is
      ' equal to C_TAG.
      ''''''''''''''''''''''''''''''''''''
      Dim Ctrl As Office.CommandBarControl
      
      On Error Resume Next
      Set Ctrl = Application.CommandBars.FindControl(Tag:=C_TAG)
      
      Do Until Ctrl Is Nothing
          Ctrl.Delete
          Set Ctrl = Application.CommandBars.FindControl(Tag:=C_TAG)
      Loop
      
      End Sub
      
      

      【讨论】:

        【解决方案4】:

        根据您文档的格式,我可能会建议您导出为 .csv 并使用它。如果您只需要数字,这将是最简单的方法。

        【讨论】:

        • 感谢您的回答。我相信我很困惑。您指的是什么文件的“格式”?我试图在我的帖子中概述这一点。其次,将我的 xls 更改为 csv 可以获得什么?再次感谢!
        猜你喜欢
        • 2014-10-01
        • 2018-11-25
        • 2018-03-21
        • 2018-12-26
        • 2012-07-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-18
        相关资源
        最近更新 更多