目前我有一个宏将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)
提前感谢您提供的任何帮助。
答案 0 :(得分:0)
最初“回答”中的问题
考虑字段1:
“固定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数据中符号的位置:
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