VBA代码将数据转换为打包格式

时间:2015-05-14 11:39:20

标签: excel-vba vba excel

目前我有一个宏将excel文件分解为固定的NL ASCII文本文件,该文件是78字节文件27924块大小。然后,我必须将文件发送给供应商,以将数据打包到66字节文件。 (包括每个打包区域前面的空格。)请参阅下面的布局。这可以在VBA中完成还是需要在编程软件中完成?

我从ASCII开始:

Field #  - Start Pos - Length
    1    -     1     -   4   
    2    -     5     -   6   
    3    -    11     -   2   
    4    -    13     -   6   
    5    -    19     -   6   
    6    -    25     -   1   
    7    -    26     -  21   
    8    -    47     -  10   
    9    -    57     -   1   
    10   -    58     -   1   
    11   -    59     -  20   

并且需要以(压缩字段)结束

Field #  - Start Pos - Length - Cobol format    
    1    -    1      -   2    - pic s9(03)comp-3
    2    -    3      -   3    - pic s9(05)comp-3
    3    -    6      -   1    - pic s9(01)comp-3
    4    -    7      -   3    - pic s9(05)comp-3
    5    -   10      -   3    - pic s9(05)comp-3
    6    -   13      -   1    - pic x(01)       
    7    -   14      -  21    - pic x(21)       
    8    -   35      -  10    - pic x(10)       
    9    -   45      -   1    - pic x(01)       
   10    -   46      -   1    - pic x(01)       
   11    -   47      -  20    - pic x(20)       

提前感谢您提供的任何帮助。

1 个答案:

答案 0 :(得分:0)

最初“回答”中的问题

考虑字段1:

  • 如果值为负123,则保持为“-123”或“123 - ”?
  • 输出格式为2个字节,对“s9(3)comp-3”是正确的。但是没有空间在“每个包装场前面的空间”。

“固定NL ASCII文本文件”是否意味着您有78行字节的行以换行符分隔?

你说你“有一个分解excel文件的宏”。您想要更新该宏还是想要读取文本文件的宏并输出Cobol文件?

请通过更新您的问题来回答我的问题

可能的答案

我认为你在工作中发布了你的问题,直到星期一你才会回答我的问题。我将在下周限制互联网访问,所以我发布了我希望是一个令人满意的答案。我会尽可能回复你的回复。

我对您的问题的解释是,您有一个宏,它构建一个358行的文件,每行78个字节由换行符终止。每行包含11个字段,其中前六个是数字。您使用转换服务创建一个未更改的文件,除了每行的六个数字字段从ASCII字符串转换为Cobol的comp3格式。

在您的宏中,您将拥有如下语句:

RowCrnt = RowCrnt & Range.Value

其中RowCrnt是一个字符串,其中正在构建行以准备输出,Range.Value是从工作表中获取的ASCII数字字符串。

我写了一个函数,所以你可以用以下代码替换上面的语句:

RowCrnt = RowCrnt & AsciiToComp3(P1, P2, P3, Range.Value)

我稍后会解释P1,P2和P3。通过此更改,宏创建的文件应采用Cobol格式。

我直观地检查了我的函数是否创建了我期望的输出。但是,我没有独立的测试方法。也许我对comp3格式的理解是错误的。也许有一个微妙的错误很难在视觉上发现。也许转换的内容比我所欣赏的更多。

假设您现有的宏为不同的数据集创建文件A1,A2等。您的供应商已将文件A1,A2和A3转换为文件B1,B2和B3。您需要检查更新的宏匹配B1,B2和B3创建的文件。

如果我的函数发现它不喜欢的东西,它使用Debug.Assert False来停止执行。这在开发过程中非常方便,但它会吓坏生产宏中的用户。这些Debug.Assert False语句都不应该到达,因为它们表示函数的使用或给予处理的数据中的错误。这些都不应该发生,所以你可以决定保持功能不变。只有你能知道什么是合适的。

参数P1,P2和P3指定要转换的ASCII值的格式。函数创建的comp3格式是保存这种ASCII值的最小格式。这对您的数据应该是令人满意的。如果您希望comp3值更大,则指定ASCII数据的最大大小大于实际最大值。

P1标识ASCII数据中符号的位置:

  • -1表示前导符号。例如:-123或+123或123。
  • 0表示无符号
  • 1表示尾随符号。例如:123-或123+或123。

Comp3格式始终为尾随符号保留半字节,即使它未被使用。

P2是最大整数位数。因此,如果最大值为999,则P2 = 3;如果最大值为99,999,则P2 = 5.

P3是最大小数位数。如果ASCII值为整数,则设置P3 = 0。在ASCII值中使用本地十进制符号(句点或逗号)。

祝你好运

Option Explicit
Function AsciiToComp3(ByVal SignFmt As Long, ByVal NumIntegerDigitsFmt, _
                      ByVal NumDecimalDigitsFmt As Long, _
                      ByVal AsciiValue As String) As String

  ' * Returns a string which is a Comp-3 version of AsciiValue.

  ' * SignFmt             -1 if AsciiValue has an optional leading sign.  For
  '                          example 12.34 or -12.34
  '                        0 if AsciiValue has no sign.  For example 12.34
  '                        1 if AsciiValue has an optional trailinging sign.
  '                          For example 12.34 or 12.34-
  ' * NumIntegerDigitsFmt  If AsciiValue may not contain a decimal separator,
  '                        the maximum number of digits.
  '                        If AsciiValue may contain a decimal separator, the
  '                        maximum number of digits to the left of the decimal
  '                        separator. The decimal separator is
  '                        Application.DecimalSeparator which returns "." or'
  '                        "," according to local conventions.
  ' * NumDecimalDigitsFmt  If AsciiValue may not contain a decimal separator, 0.
  '                        If AsciiValue may contain a decimal separator, the
  '                        maximum number of digits to the right of the decimal
  '                        separator.
  ' * AsciiValue           The value to be converted to comp-3 format. This
  '                        string must confirm to the format defined by the
  '                        first three parameters. For example: it can only
  '                        contain a sign if SignFmt indicates it can; it can
  '                        only contain a decimal separator if
  '                        NumDecimalDigitsFmt > 0 and the number of digits
  '                        cannot exceed the numbers indicated by
  '                        NumIntegerDigitsFmt and NumDecimalDigitsFmt.

  ' The return string will be sized to hold the largest Ascii value. Increase
  ' NumIntegerDigits and NumDecimalDigits above the true maximum size of
  ' AsciiValue if a larger return string is required. If the return value is to
  ' be signed but AsciiValue is not signed then set Sign to -1 or 1 as though
  ' it could be signed.

  ' The number of bytes in the return string will be:
  '    (NumIntegerDigits + NumDecimalDigits + 1) / 2 rounded up.
  ' The "+ 1" is for the sign.  Space is always allocated for a sign even if
  ' the value is unsigned.
  ' If the expression has to be rounded up, there will be space for one more
  ' integer digit than requested.

  Dim Digit As String
  Dim NumericValue As String
  Dim PosDest As Long
  Dim Pos As Long
  Dim PosDecimal As Long
  Dim Positive As Boolean
  Dim PosSrc As Long
  Dim ReturnValue As String

  ' Enlarge NumIntegerDigits if necessary
  If (NumIntegerDigitsFmt + NumDecimalDigitsFmt + 1) Mod 2 = 1 Then
    ' Make output string a whole number of bytes
    NumIntegerDigitsFmt = NumIntegerDigitsFmt + 1
  End If

  ' Decode AsciiValue

  ' If sign present, record value and remove from AsciiValue
  Select Case SignFmt
    Case -1
      Select Case Left(AsciiValue, 1)
        Case "-"
          Positive = False
          AsciiValue = Mid(AsciiValue, 2)  ' Remove sign
        Case "+"
          Positive = True
          AsciiValue = Mid(AsciiValue, 2)  ' Remove sign
        Case Else
          Positive = True
      End Select
    Case 0
      Positive = True
    Case 1
      Select Case Right(AsciiValue, 1)
        Case "-"
          Positive = False
          AsciiValue = Mid(AsciiValue, 1, Len(AsciiValue) - 1) ' Remove sign
        Case "+"
          Positive = True
          AsciiValue = Mid(AsciiValue, 1, Len(AsciiValue) - 1) ' Remove sign
        Case Else
          Positive = True
      End Select
    Case Else
      Debug.Assert False    ' Call error. SignFmt not -1, 0 or 1.
  End Select

  PosDecimal = InStr(1, AsciiValue, Application.DecimalSeparator)

  If PosDecimal > 0 Then
    If NumDecimalDigitsFmt = 0 Then
      ' AsciiValue contains decimal separator but specification says it
      ' should not
      Debug.Assert False
      AsciiToComp3 = "Error"
      Exit Function
    End If
    If PosDecimal > NumIntegerDigitsFmt Or _
       Len(AsciiValue) - PosDecimal > NumDecimalDigitsFmt Then
      ' AsciiValue contains more integer digits or more decimal digits than
      ' allowed by specification
      Debug.Assert False
      AsciiToComp3 = "Error"
      Exit Function
    End If
    ' Pad integer part of AsciiValue with leading zeros to NumIntegerDigits,
    ' discard decimal and
    ' pad decimal part with trailing digits to NumDecimalDigits
    NumericValue = Right(String(NumIntegerDigitsFmt, "0") & Mid(AsciiValue, 1, PosDecimal - 1), NumIntegerDigitsFmt) & _
                   Left(Mid(AsciiValue, PosDecimal + 1) & String(NumDecimalDigitsFmt, "0"), NumDecimalDigitsFmt)
  Else
    ' AsciiValue is all integer
    If Len(AsciiValue) > NumIntegerDigitsFmt Then
      ' AsciiValur contains more integer digits than allowed by specification
      Debug.Assert False
      AsciiToComp3 = "Error"
      Exit Function
    End If
    ' Pad AsciiValue with leading zeros to maximum length
    NumericValue = Right(String(NumIntegerDigitsFmt, "0") & AsciiValue, NumIntegerDigitsFmt)
    If NumDecimalDigitsFmt > 0 Then
      NumericValue = NumericValue & String(NumDecimalDigitsFmt, "0")
    End If
  End If

  ' Create Return value of required length but full of zeros.
  ReturnValue = String((NumIntegerDigitsFmt + NumDecimalDigitsFmt + 1) \ 2, Chr$(0))

  ' Pack Ascii string "ABCEFG" with each pair of ASCII digits becoming one byte
  ' of output string.  Ascii string has odd number of digits with last half byte
  ' reserved for sign.
  ' ABCDEFG  -> ab cd ef g
  PosSrc = 1
  PosDest = 1
  Do While True
    ' Handle digits that are stored in lefthand half of bytes
    Digit = Mid(NumericValue, PosSrc, 1)
    If Digit < "0" Or Digit > "9" Then
     ' Not a decimal digit
      Debug.Assert False
      AsciiToComp3 = "Error"
      Exit Function
    End If
    Mid(ReturnValue, PosDest, 1) = Chr(Val(Digit) * 16)

    PosSrc = PosSrc + 1
    If PosSrc > Len(NumericValue) Then
      ' All digits handled
      Exit Do
    End If

    ' Handle digits that are stored in righthand half of bytes
    Digit = Mid(NumericValue, PosSrc, 1)
    If Digit < "0" Or Digit > "9" Then
     ' Not a decimal digit
      Debug.Assert False
      AsciiToComp3 = "Error"
      Exit Function
    End If
    Mid(ReturnValue, PosDest, 1) = Chr$(Asc(Mid(ReturnValue, PosDest, 1)) Or Val(Digit))

    PosSrc = PosSrc + 1
    PosDest = PosDest + 1

  Loop

  If SignFmt = 0 Then
    ' Comp3 string is unsigned.  Half byte for sign already zero so no action required.
  Else
    If Positive Then
      Mid(ReturnValue, PosDest, 1) = Chr$(Asc(Mid(ReturnValue, PosDest, 1)) Or &HC)
    Else
      Mid(ReturnValue, PosDest, 1) = Chr$(Asc(Mid(ReturnValue, PosDest, 1)) Or &HD)
    End If
  End If

  AsciiToComp3 = ReturnValue

End Function
相关问题