我不清楚“COMP-3 加密文件”一词的含义,但我认为您是说您有一个文件从基于 zOS (EBCDIC) 的系统传输到基于 ASCII 的系统,并且您希望成为能够处理包含在 COMP-3(压缩十进制字段)中的值。如果这是正确的,我有一些与您的需求相关的代码和研究。
我假设文件从 zOS 传输时已从 EBCDIC 转换为 ASCII。
一个常见的误解是,如果将 COMP-3(压缩十进制)数据从 EBCDIC 转换为 ASCII,就会“损坏”。事实并非如此。你得到的是范围从 x'00' - x'0F' 的值。无论您使用的是基于 EBCDIC 还是基于 ASCII 的系统,该范围内的十六进制值都是相同的。
如果在 [任一系统上] 的十六进制编辑器之外查看数据,则它似乎已损坏。根据代码页,压缩十进制数 01234567890 可能显示为⌁杅ྉ。但是,使用十六进制编辑器可以看到该值实际上是 x'01 23 45 67 89 0F'。两个数字存储在一个字节中(每个半字节中的一个数字,最后一个字节中的最后一个半字节为符号)。当每个字节从十六进制转换时,将返回实际数字。例如,使用 Lua,如果变量 iChar 包含 x'23',则函数 oDec = string.format("%X", iChar) 返回文本值“23”,可以将其转换为数字。通过遍历 x'01 23 45 67 89 0F' 的整个字符串,返回实际数字 (01234567890)。可以通过反转过程来“重新打包”该号码。
解压压缩十进制字段的示例代码如下所示:
--[[ Lua 5.2.3 ]]
--[[ Author: David Alley
Written: August 9, 2017 ]]
--[[ Begin Function ]]
function xdec_unpack (iHex, lField, lNumber)
--[[
This function reads packed decimal data (converted from EBCDIC to ASCII) as input
and returns unpacked ASCII decimal numbers.
--]]
if iHex == nil or iHex == ""
then
return iHex
end
local aChar = {}
local aUnpack = {}
local iChar = ''
for i = 1, lField do
aChar[i] = string.byte(iHex, i)
end
for i, iChar in ipairs(aChar) do
local oDec = string.format("%X", iChar)
if string.len(oDec) == 1
then
table.insert(aUnpack, "0" .. oDec) --[[ Handles binary zeros ]]
else
table.insert(aUnpack, oDec)
end
end
if string.len(table.concat(aUnpack)) - 1 ~= lNumber
then
aUnpack[1] = string.sub(aUnpack[1],2,2)
end
return table.concat(aUnpack)
end
--[[ End Function xdec_unpack ]]
--[[ The code below was written for Linux and reads an entire file. It assumes that there is only one field, and that
field is in packed decimal format. Packed decimal format means that the file was transferred from a z/OS (EBCDIC) system
and the data was converted to ASCII.
It is necessary to supply the field length because when Lua encounters binary zeros (common in packed decimal),
they are treated as an "end of record" indicator. The packed length value is supplied by the variable lField and the
unpacked length value is supplied by the variable lNumber.
Since there is only one field, that field by default, is at the end of each record (the field is the record). Therefore,
any "new line" values (0x0a for Linux) must be included when reading records. This is handled by adding 1 to the variable
lField when reading records. Therefore, this code must be modified if there are multiple fields, and/or the packed decimal
field is not the last field in the record.
The sign is dropped from the unpacked value that is returned from the function xdec_unpack by removing the last byte from the
variable Output1 before writing the records. Therefore, this code must be modified if it is necessary to process negative
numbers. ]]
local lField = 7 --[[ This variable is the length of the packed decimal field before unpacking and is required by the
xdec_unpack function. ]]
local lNumber = 12 --[[ This variable is the length of the unpacked decimal field not including the sign. It is required by the
xdec_unpack function. Its purpose is to determine if a high order zero (left zero) is to be removed. This
occurs in situations where the packed decimal field contains an even number of digits. For example,
0123456789. ]]
local sFile = io.open("/home/david/Documents/Lua/Input/Input2.txt", "r")
local oFile = io.open("/home/david/Documents/Lua/Input/Output1.txt", "w")
while true do
sFile:seek("cur")
local sLine = sFile:read(lField + 1)
if sLine == nil then break end
local Output1 = xdec_unpack(sLine, lField, lNumber) --[[ Call function to unpack ]]
Output1 = string.sub(Output1,1, #Output1 - 1) --[[ Remove sign ]]
oFile:write(Output1, "\n")
end
sFile:close()
oFile:close()