6

メインフレームから入ってくる一連のASCIIフラットファイルがC#アプリケーションで処理されています。Packed Decimal(COMP-3)フィールドを備えた新しいフィードが導入されました。このフィールドは、数値に変換する必要があります。

ファイルは、ASCII転送モードを使用してFTP経由で転送されています。バイナリフィールドに、値の代わりに非常に低いASCIIコードまたは制御文字として解釈されるものが含まれている可能性があることを懸念しています。さらに悪いことに、FTPプロセスで失われる可能性があります。

さらに、フィールドは文字列として読み取られています。私はこの部分(つまり、ある種のストリーム)を回避する柔軟性を持っているかもしれませんが、ビジネスは私にプッシュバックを与えます。

要件は「HEXからASCIIに変換する」と読みましたが、明らかに正しい値が得られませんでした。どんな助けでもいただければ幸いです。変換プロセスのロジックを説明できる限り、言語固有である必要はありません。

4

8 に答える 8

9

Comp-3 BCD データを「レガシー」メインフレーム ファイルから C# で使用できるものに変換することに関する多数の掲示板の投稿を見てきました。最初に、私はこれらの投稿のいくつかが受け取った反応にあまり魅力を感じていないことを言いたいと思います。ある種の COBOL 規則についての回答が必要な場合は、COBOL 向けのサイトにアクセスしてみませんか。」私にとって、これは完全な BS です。ソフトウェア開発者は、おそらく今後何年もの間 (残念ながら)、現実の世界に存在するこれらのレガシー問題のいくつかに対処する方法を理解する必要があるからです。したがって、この投稿で次のコードを非難されたとしても、

最初に、IBM のような従来のメインフレーム システムから読み取ったファイルは、データを EBCDIC 形式で表示し、そのデータを処理できる C#/C++ 文字列に変換することを理解してください。データを ASCII 形式に変換するには、適切なコード ページ変換を使用する必要があります。これを処理する方法の良い例は次のとおりです。

StreamReader readFile = new StreamReader(path, Encoding.GetEncoding(037); // 037 = EBCDIC から ASCII への変換。

これにより、このストリームから読み取ったものはすべて ASCII に変換され、文字列形式で使用できるようになります。これには、COBOL で宣言されている "Zoned Decimal" (Pic 9) および "Text" (Pic X) フィールドが含まれます。ただし、これは、char[] または byte[] 配列に読み込まれたときに、COMP-3 フィールドを正しい「バイナリ」等価物に必ずしも変換するとは限りません。これを行うには、(UTF-8、UTF-16、デフォルトなどを使用しても) コード ページを適切に変換する唯一の方法として、次のようにファイルを開く必要があります。

FileStream fileStream = new FileStream(path, FIleMode.Open, FIleAccess.Read, FileShare.Read);

もちろん、「FileShare.Read」オプションは「オプション」です。

10 進数値に変換したいフィールドを分離したら (その後、必要に応じて ASCII 文字列に変換)、次のコードを使用できます。で取得できます:

http://www.microsoft.com/downloads/details.aspx?familyid=0e4bba52-cc52-4d89-8590-cda297ff7fbd&displaylang=en

私は、このロジックの最も重要な部分を分離 (私が思うに) し、必要なことを実行できる 2 つの方法に統合しました。私の目的のために、これを Decimal 値を返すものとして残すことにしました。基本的に、このメソッドは「unpack」と呼ばれ、byte[] 配列 (12 バイト以内) とスケールを int として渡します。これは、Decimal 値で返す小数点以下の桁数です。これが私と同じようにあなたにとってもうまくいくことを願っています。

    private Decimal Unpack(byte[] inp, int scale)
    {
        long lo = 0;
        long mid = 0;
        long hi = 0;
        bool isNegative;

        // this nybble stores only the sign, not a digit.  
        // "C" hex is positive, "D" hex is negative, and "F" hex is unsigned. 
        switch (nibble(inp, 0))
        {
            case 0x0D:
                isNegative = true;
                break;
            case 0x0F:
            case 0x0C:
                isNegative = false;
                break;
            default:
                throw new Exception("Bad sign nibble");
        }
        long intermediate;
        long carry;
        long digit;
        for (int j = inp.Length * 2 - 1; j > 0; j--)
        {
            // multiply by 10
            intermediate = lo * 10;
            lo = intermediate & 0xffffffff;
            carry = intermediate >> 32;
            intermediate = mid * 10 + carry;
            mid = intermediate & 0xffffffff;
            carry = intermediate >> 32;
            intermediate = hi * 10 + carry;
            hi = intermediate & 0xffffffff;
            carry = intermediate >> 32;
            // By limiting input length to 14, we ensure overflow will never occur

            digit = nibble(inp, j);
            if (digit > 9)
            {
                throw new Exception("Bad digit");
            }
            intermediate = lo + digit;
            lo = intermediate & 0xffffffff;
            carry = intermediate >> 32;
            if (carry > 0)
            {
                intermediate = mid + carry;
                mid = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                if (carry > 0)
                {
                    intermediate = hi + carry;
                    hi = intermediate & 0xffffffff;
                    carry = intermediate >> 32;
                    // carry should never be non-zero. Back up with validation
                }
            }
        }
        return new Decimal((int)lo, (int)mid, (int)hi, isNegative, (byte)scale);
    }

    private int nibble(byte[] inp, int nibbleNo)
    {
        int b = inp[inp.Length - 1 - nibbleNo / 2];
        return (nibbleNo % 2 == 0) ? (b & 0x0000000F) : (b >> 4);
    }

質問がある場合は、ここに投稿してください。今日の問題に関連する質問を投稿することを選択した他のすべての人のように、私は「炎上」するのではないかと思うからです...

ありがとう、ジョン - 長老。

于 2012-04-17T22:56:15.070 に答える
6

まず最初に、ASCII転送モードによって引き起こされる行末(EOL)変換の問題を排除する必要があります。BCD値がたまたまEOL文字に対応している場合、データの破損を心配するのは絶対に正しいことです。この問題の最悪の側面は、それがめったにそして予期せずに起こるということです。

最善の解決策は、転送モードをBINに変更することです。転送するデータはバイナリであるため、これは適切です。正しいFTP転送モードを使用できない場合は、コードでASCIIモードの損傷を元に戻すことができます。あなたがしなければならないのは、\ r\nペアを\nに戻すことだけです。もし私があなたなら、これが十分にテストされていることを確認します。

EOLの問題に対処したら、COMP-3変換はかなり簡単です。この記事は、BASICのサンプルコードを使用してMSナレッジベースで見つけることができました。このコードのVB.NETポートについては、以下を参照してください。

COMP-3値を扱っているので、読んでいるファイル形式はほぼ確実に固定レコードサイズと固定フィールド長を持っています。もし私があなたなら、これを先に進める前に、ファイル形式の仕様を手に入れるでしょう。このデータを操作するには、BinaryReaderを使用する必要があります。誰かがこの点を押し返しているなら、私は立ち去ります。彼らに彼らの愚かさを甘やかすために他の誰かを見つけさせてください。

これがBASICサンプルコードのVB.NETポートです。COMP-3ファイルにアクセスできないため、これはテストしていません。これが機能しない場合は、ガイダンスとして元のMSサンプルコードを参照するか、この質問に対する他の回答の参照を参照してください。

Imports Microsoft.VisualBasic

Module Module1

'Sample COMP-3 conversion code
'Adapted from http://support.microsoft.com/kb/65323
'This code has not been tested

Sub Main()

    Dim Digits%(15)       'Holds the digits for each number (max = 16).
    Dim Basiceqv#(1000)   'Holds the Basic equivalent of each COMP-3 number.

    'Added to make code compile
    Dim MyByte As Char, HighPower%, HighNibble%
    Dim LowNibble%, Digit%, E%, Decimal%, FileName$


    'Clear the screen, get the filename and the amount of decimal places
    'desired for each number, and open the file for sequential input:
    FileName$ = InputBox("Enter the COBOL data file name: ")
    Decimal% = InputBox("Enter the number of decimal places desired: ")

    FileOpen(1, FileName$, OpenMode.Binary)

    Do Until EOF(1)   'Loop until the end of the file is reached.
        Input(1, MyByte)
        If MyByte = Chr(0) Then     'Check if byte is 0 (ASC won't work on 0).
            Digits%(HighPower%) = 0       'Make next two digits 0. Increment
            Digits%(HighPower% + 1) = 0   'the high power to reflect the
            HighPower% = HighPower% + 2   'number of digits in the number
            'plus 1.
        Else
            HighNibble% = Asc(MyByte) \ 16      'Extract the high and low
            LowNibble% = Asc(MyByte) And &HF    'nibbles from the byte. The
            Digits%(HighPower%) = HighNibble%  'high nibble will always be a
            'digit.
            If LowNibble% <= 9 Then                   'If low nibble is a
                'digit, assign it and
                Digits%(HighPower% + 1) = LowNibble%   'increment the high
                HighPower% = HighPower% + 2            'power accordingly.
            Else
                HighPower% = HighPower% + 1 'Low nibble was not a digit but a
                Digit% = 0                  '+ or - signals end of number.

                'Start at the highest power of 10 for the number and multiply
                'each digit by the power of 10 place it occupies.
                For Power% = (HighPower% - 1) To 0 Step -1
                    Basiceqv#(E%) = Basiceqv#(E%) + (Digits%(Digit%) * (10 ^ Power%))
                    Digit% = Digit% + 1
                Next

                'If the sign read was negative, make the number negative.
                If LowNibble% = 13 Then
                    Basiceqv#(E%) = Basiceqv#(E%) - (2 * Basiceqv#(E%))
                End If

                'Give the number the desired amount of decimal places, print
                'the number, increment E% to point to the next number to be
                'converted, and reinitialize the highest power.
                Basiceqv#(E%) = Basiceqv#(E%) / (10 ^ Decimal%)
                Print(Basiceqv#(E%))
                E% = E% + 1
                HighPower% = 0
            End If
        End If
    Loop

    FileClose()   'Close the COBOL data file, and end.
End Sub

End Module
于 2008-09-27T04:56:52.893 に答える
3

元のデータが EBCDIC であった場合、COMP-3 フィールドは文字化けしています。FTP プロセスは、COMP-3 フィールド内のバイト値の EBCDIC から ASCII への変換を実行しましたが、これは必要なものではありません。これを修正するには、次のことができます。

1) 転送に BINARY モードを使用して、生の EBCDIC データを取得します。次に、COMP-3 フィールドを数値に変換し、レコード上のその他の EBCDIC テキストを ASCII に変換します。パック フィールドは、各桁を半バイトに格納し、下位半バイトを符号として格納します (F は正の値で、通常は D または E は負の値です)。123.4 を PIC 999.99 USAGE COMP-3 に格納すると、X'01234F' (3 バイト) になり、同じフィールドの -123 は X'01230D' になります。

2) 送信者にフィールドを USAGE IS DISPLAY SIGN IS LEADING (または TRAILING) 数値フィールドに変換させます。これは、数字を EBCDIC 数字の文字列として格納し、符号は個別の負 (-) または空白文字として格納します。FTP 転送では、すべての数字と記号が対応する ASCII に正しく変換されます。

于 2008-11-04T19:48:21.557 に答える
2

ここでベースから大きく外れてしまったことをお詫びしますが、ここに貼り付けるこのコードサンプルが役立つかもしれません。これはVBRocksから来ました...

Imports System
Imports System.IO
Imports System.Text
Imports System.Text.Encoding



'4/20/07 submission includes a line spacing addition when a control character is used:
'   The line spacing is calculated off of the 3rd control character.
'
'   Also includes the 4/18 modification of determining end of file.

'4/26/07 submission inclues an addition of 6 to the record length when the 4th control
'   character is an 8.  This is because these records were being truncated.


'Authored by Gary A. Lima, aka. VBRocks



''' <summary>
''' Translates an EBCDIC file to an ASCII file.
''' </summary>
''' <remarks></remarks>
Public Class EBCDIC_to_ASCII_Translator

#Region " Example"

    Private Sub Example()
        'Set your source file and destination file paths
        Dim sSourcePath As String = "c:\Temp\MyEBCDICFile"
        Dim sDestinationPath As String = "c:\Temp\TranslatedFile.txt"

        Dim trans As New EBCDIC_to_ASCII_Translator()

        'If your EBCDIC file uses Control records to determine the length of a record, then this to True
        trans.UseControlRecord = True

        'If the first record of your EBCDIC file is filler (junk), then set this to True
        trans.IgnoreFirstRecord = True

        'EBCDIC files are written in block lengths, set your block length (Example:  134, 900, Etc.)
        trans.BlockLength = 900

        'This method will actually translate your source file and output it to the specified destination file path
        trans.TranslateFile(sSourcePath, sDestinationPath)


        'Here is a alternate example:
        'No Control record is used
        'trans.UseControlRecord = False

        'Translate the whole file, including the first record
        'trans.IgnoreFirstRecord = False

        'Set the block length
        'trans.BlockLength = 134

        'Translate...
        'trans.TranslateFile(sSourcePath, sDestinationPath)



        '*** Some additional methods that you can use are:

        'Trim off leading characters from left side of string (position 0 to...)
        'trans.LTrim = 15

        'Translate 1 EBCDIC character to an ASCII character
        'Dim strASCIIChar as String = trans.TranslateCharacter("S")

        'Translate an EBCDIC character array to an ASCII string
        'trans.TranslateCharacters(chrEBCDICArray)

        'Translates an EBCDIC string to an ASCII string
        'Dim strASCII As String = trans.TranslateString("EBCDIC String")


    End Sub

#End Region    'Example

    'Translate characters from EBCDIC to ASCII

    Private ASCIIEncoding As Encoding = Encoding.ASCII
    Private EBCDICEncoding As Encoding = Encoding.GetEncoding(37)  'EBCDIC

    'Block Length:  Can be fixed (Ex:  134). 
    Private miBlockLength As Integer = 0
    Private mbUseControlRec As Boolean = True        'If set to False, will return exact block length
    Private mbIgnoreFirstRecord As Boolean = True    'Will Ignore first record if set to true  (First record may be filler)
    Private miLTrim As Integer = 0

    ''' <summary>
    ''' Translates SourceFile from EBCDIC to ASCII.  Writes output to file path specified by DestinationFile parameter.
    ''' Set the BlockLength Property to designate block size to read.
    ''' </summary>
    ''' <param name="SourceFile">Enter the path of the Source File.</param>
    ''' <param name="DestinationFile">Enter the path of the Destination File.</param>
    ''' <remarks></remarks>
    Public Sub TranslateFile(ByVal SourceFile As String, ByVal DestinationFile As String)

        Dim iRecordLength As Integer     'Stores length of a record, not including the length of the Control Record (if used)
        Dim sRecord As String = ""         'Stores the actual record
        Dim iLineSpace As Integer = 1    'LineSpace:  1 for Single Space, 2 for Double Space, 3 for Triple Space...

        Dim iControlPosSix As Byte()      'Stores the 6th character of a Control Record (used to calculate record length)
        Dim iControlRec As Byte()          'Stores the EBCDIC Control Record (First 6 characters of record)
        Dim bEOR As Boolean                'End of Record Flag
        Dim bBOF As Boolean = True      'Beginning of file
        Dim iConsumedChars As Integer = 0     'Stores the number of consumed characters in the current block
        Dim bIgnoreRecord As Boolean = mbIgnoreFirstRecord   'Ignores the first record if set.

        Dim ControlArray(5) As Char         'Stores Control Record (first 6 bytes)
        Dim chrArray As Char()              'Stores characters just after read from file

        Dim sr As New StreamReader(SourceFile, EBCDICEncoding)
        Dim sw As New StreamWriter(DestinationFile)

        'Set the RecordLength to the RecordLength Property (below)
        iRecordLength = miBlockLength

        'Loop through entire file
        Do Until sr.EndOfStream = True

            'If using a Control Record, then check record for valid data.
            If mbUseControlRec = True Then
                'Read the Control Record (first 6 characters of the record)
                sr.ReadBlock(ControlArray, 0, 6)

                'Update the value of consumed (read) characters
                iConsumedChars += ControlArray.Length

                'Get the bytes of the Control Record Array
                iControlRec = EBCDICEncoding.GetBytes(ControlArray)

                'Set the line spacing  (position 3 divided by 64)
                '   (64 decimal = Single Spacing; 128 decimal = Double Spacing)
                iLineSpace = iControlRec(2) / 64


                'Check the Control record for End of File
                'If the Control record has a 8 or 10 in position 1, and a 1 in postion 2, then it is the end of the file
                If (iControlRec(0) = 8 OrElse iControlRec(0) = 10) AndAlso _
                    iControlRec(1) = 1 Then

                    If bBOF = False Then
                        Exit Do

                    Else
                        'The Beginning of file flag is set to true by default, so when the first
                        '   record is encountered, it is bypassed and the bBOF flag is set to False
                        bBOF = False

                    End If    'If bBOF = Fals

                End If    'If (iControlRec(0) = 8 OrElse



                'Set the default value for the End of Record flag to True
                '   If the Control Record has all zeros, then it's True, else False
                bEOR = True

                'If the Control record contains all zeros, bEOR will stay True, else it will be set to False
                For i As Integer = 0 To 5
                    If iControlRec(i) > 0 Then
                        bEOR = False

                        Exit For

                    End If    'If iControlRec(i) > 0

                Next    'For i As Integer = 0 To 5

                If bEOR = False Then
                    'Convert EBCDIC character to ASCII
                    'Multiply the 6th byte by 6 to get record length
                    '   Why multiply by 6?  Because it works.
                    iControlPosSix = EBCDICEncoding.GetBytes(ControlArray(5))

                    'If the 4th position of the control record is an 8, then add 6
                    '    to the record length to pick up remaining characters.
                    If iControlRec(3) = 8 Then
                        iRecordLength = CInt(iControlPosSix(0)) * 6 + 6

                    Else
                        iRecordLength = CInt(iControlPosSix(0)) * 6

                    End If

                    'Add the length of the record to the Consumed Characters counter
                    iConsumedChars += iRecordLength

                Else
                    'If the Control Record had all zeros in it, then it is the end of the Block.

                    'Consume the remainder of the block so we can continue at the beginning of the next block.
                    ReDim chrArray(miBlockLength - iConsumedChars - 1)
                    'ReDim chrArray(iRecordLength - iConsumedChars - 1)

                    'Consume (read) the remaining characters in the block.  
                    '   We are not doing anything with them because they are not actual records.
                    'sr.ReadBlock(chrArray, 0, iRecordLength - iConsumedChars)
                    sr.ReadBlock(chrArray, 0, miBlockLength - iConsumedChars)

                    'Reset the Consumed Characters counter
                    iConsumedChars = 0

                    'Set the Record Length to 0 so it will not be processed below.
                    iRecordLength = 0

                End If    ' If bEOR = False

            End If    'If mbUseControlRec = True



            If iRecordLength > 0 Then
                'Resize our array, dumping previous data.  Because Arrays are Zero (0) based, subtract 1 from the Record length.
                ReDim chrArray(iRecordLength - 1)

                'Read the specfied record length, without the Control Record, because we already consumed (read) it.
                sr.ReadBlock(chrArray, 0, iRecordLength)

                'Copy Character Array to String Array, Converting in the process, then Join the Array to a string
                sRecord = Join(Array.ConvertAll(chrArray, New Converter(Of Char, String)(AddressOf ChrToStr)), "")

                'If the record length was 0, then the Join method may return Nothing
                If IsNothing(sRecord) = False Then

                    If bIgnoreRecord = True Then
                        'Do nothing - bypass record

                        'Reset flag
                        bIgnoreRecord = False

                    Else
                        'Write the line out, LTrimming the specified number of characters.
                        If sRecord.Length >= miLTrim Then
                            sw.WriteLine(sRecord.Remove(0, miLTrim))

                        Else
                            sw.WriteLine(sRecord.Remove(0, sRecord.Length))

                        End If    ' If sRecord.Length >= miLTrim

                        'Write out the number of blank lines specified by the 3rd control character.
                        For i As Integer = 1 To iLineSpace - 1
                            sw.WriteLine("")

                        Next    'For i As Integer = 1 To iLineSpace

                    End If    'If bIgnoreRecord = True


                    'Obviously, if we have read more characters from the file than the designated size of the block,
                    '   then subtract the number of characters we have read into the next block from the block size.
                    If iConsumedChars > miBlockLength Then
                        'If iConsumedChars > iRecordLength Then
                        iConsumedChars = iConsumedChars - miBlockLength
                        'iConsumedChars = iConsumedChars - iRecordLength

                    End If

                End If    'If IsNothing(sRecord) = False

            End If    'If iRecordLength > 0

            'Allow computer to process  (works in a class module, not in a dll)
            'Application.DoEvents()

        Loop

        'Destroy StreamReader (sr)
        sr.Close()
        sr.Dispose()

        'Destroy StreamWriter (sw)
        sw.Close()
        sw.Dispose()

    End Sub



    ''' <summary>
    ''' Translates 1 EBCDIC Character (Char) to an ASCII String
    ''' </summary>
    ''' <param name="chr"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function ChrToStr(ByVal chr As Char) As String
        Dim sReturn As String = ""

        'Convert character into byte
        Dim EBCDICbyte As Byte() = EBCDICEncoding.GetBytes(chr)

        'Convert EBCDIC byte to ASCII byte
        Dim ASCIIByte As Byte() = Encoding.Convert(EBCDICEncoding, ASCIIEncoding, EBCDICbyte)

        sReturn = Encoding.ASCII.GetString(ASCIIByte)

        Return sReturn

    End Function



    ''' <summary>
    ''' Translates an EBCDIC String to an ASCII String
    ''' </summary>
    ''' <param name="sStringToTranslate"></param>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Function TranslateString(ByVal sStringToTranslate As String) As String
        Dim i As Integer = 0
        Dim sReturn As New System.Text.StringBuilder()

        'Loop through the string and translate each character
        For i = 0 To sStringToTranslate.Length - 1
            sReturn.Append(ChrToStr(sStringToTranslate.Substring(i, 1)))

        Next

        Return sReturn.ToString()


    End Function



    ''' <summary>
    ''' Translates 1 EBCDIC Character (Char) to an ASCII String
    ''' </summary>
    ''' <param name="sCharacterToTranslate"></param>
    ''' <returns>String</returns>
    ''' <remarks></remarks>
    Public Function TranslateCharacter(ByVal sCharacterToTranslate As Char) As String

        Return ChrToStr(sCharacterToTranslate)

    End Function



    ''' <summary>
    ''' Translates an EBCDIC Character (Char) Array to an ASCII String
    ''' </summary>
    ''' <param name="sCharacterArrayToTranslate"></param>
    ''' <returns>String</returns>
    ''' <remarks>Remarks</remarks>
    Public Function TranslateCharacters(ByVal sCharacterArrayToTranslate As Char()) As String
        Dim sReturn As String = ""

        'Copy Character Array to String Array, Converting in the process, then Join the Array to a string
        sReturn = Join(Array.ConvertAll(sCharacterArrayToTranslate, _
                            New Converter(Of Char, String)(AddressOf ChrToStr)), "")

        Return sReturn

    End Function


    ''' <summary>
    ''' Block Length must be set.  You can set the BlockLength for specific block sizes (Ex:  134).
    ''' Set UseControlRecord = False for files with specific block sizes (Default is True)
    ''' </summary>
    ''' <value>0</value>
    ''' <returns>Integer</returns>
    ''' <remarks></remarks>
    Public Property BlockLength() As Integer
        Get
            Return miBlockLength

        End Get
        Set(ByVal value As Integer)
            miBlockLength = value

        End Set
    End Property



    ''' <summary>
    ''' Determines whether a ControlKey is used to calculate RecordLength of valid data
    ''' </summary>
    ''' <value>Default value is True</value>
    ''' <returns>Boolean</returns>
    ''' <remarks></remarks>
    Public Property UseControlRecord() As Boolean
        Get
            Return mbUseControlRec

        End Get
        Set(ByVal value As Boolean)
            mbUseControlRec = value

        End Set
    End Property



    ''' <summary>
    ''' Ignores first record if set (Default is True)
    ''' </summary>
    ''' <value>Default is True</value>
    ''' <returns>Boolean</returns>
    ''' <remarks></remarks>
    Public Property IgnoreFirstRecord() As Boolean
        Get
            Return mbIgnoreFirstRecord

        End Get

        Set(ByVal value As Boolean)
            mbIgnoreFirstRecord = value

        End Set
    End Property



    ''' <summary>
    ''' Trims the left side of every string the specfied number of characters.  Default is 0.
    ''' </summary>
    ''' <value>Default is 0.</value>
    ''' <returns>Integer</returns>
    ''' <remarks></remarks>
    Public Property LTrim() As Integer
        Get
            Return miLTrim

        End Get

        Set(ByVal value As Integer)
            miLTrim = value

        End Set
    End Property


End Class
于 2008-09-27T04:35:32.057 に答える
0

パックされたフィールドは、EBCDICまたはASCIIで同じです。それらでEBCDICからASCIIへの変換を実行しないでください。.Netでは、それらをbyte[]にダンプします。

ビット単位のマスクとシフトを使用して、パック/アンパックします。-ただし、ビット演算は.Netの整数型にのみ適用されるため、いくつかのフープをジャンプする必要があります。

優れたCOBOLまたはCのアーティストは、正しい方向を示すことができます。

老人の一人を見つけて会費を払ってください(約3本のビールがそれをするべきです)。

于 2010-01-11T04:13:55.847 に答える
0

「ASCII 転送タイプ」は、ファイルを通常のテキスト ファイルとして転送します。そのため、パック 10 進数またはバイナリ データ ファイルを ASCII 転送タイプで転送すると、ファイルが破損します。「バイナリ転送タイプ」は、ファイルをテキストデータではなくバイナリデータとして扱うバイナリモードでデータを転送します。したがって、ここでは Binary 転送タイプを使用する必要があります。参考:https ://www.codeproject.com/Tips/673240/EBCDIC-to-ASCII-Converter

ファイルの準備ができたら、パック 10 進数を人間が読める 10 進数に変換するコードを次に示します。

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

   namespace ConsoleApp2
   {
    class Program
    {
        static void Main(string[] args)
        {
            var path = @"C:\FileName.BIN.dat";
            var templates = new List<Template>
            {
                new Template{StartPos=1,CharLength=4,Type="AlphaNum"},
                new Template{StartPos=5,CharLength=1,Type="AlphaNum"},
                new Template{StartPos=6,CharLength=8,Type="AlphaNum"},
                new Template{StartPos=14,CharLength=1,Type="AlphaNum"},
                new Template{StartPos=46,CharLength=4,Type="Packed",DecimalPlace=2},
                new Template{StartPos=54,CharLength=5,Type="Packed",DecimalPlace=0},
                new Template{StartPos=60,CharLength=4,Type="Packed",DecimalPlace=2},
                new Template{StartPos=64,CharLength=1,Type="AlphaNum"}
            };

            var allBytes = File.ReadAllBytes(path);
            for (int i = 0; i < allBytes.Length; i += 66)
            {
                var IsLastline = (allBytes.Length - i) < 66;
                var lineLength = IsLastline ? 64 : 66;
                byte[] lineBytes = new byte[lineLength];
                Array.Copy(allBytes, i, lineBytes, 0, lineLength);


                var outArray = new string[templates.Count];
                int index = 0;
                foreach (var temp in templates)
                {
                    byte[] amoutBytes = new byte[temp.CharLength];
                    Array.Copy(lineBytes, temp.StartPos - 1, amoutBytes, 0, 
    temp.CharLength);
                    var final = "";
                    if (temp.Type == "Packed")
                    {
                        final = Unpack(amoutBytes, temp.DecimalPlace).ToString();
                    }
                    else
                    {
                        final = ConvertEbcdicString(amoutBytes);
                    }

                    outArray[index] = final;
                    index++;

                }

                Console.WriteLine(string.Join(" ", outArray));

            }

            Console.ReadLine();
        }


        private static string ConvertEbcdicString(byte[] ebcdicBytes)
        {
            if (ebcdicBytes.All(p => p == 0x00 || p == 0xFF))
            {
                //Every byte is either 0x00 or 0xFF (fillers)
                return string.Empty;
            }

            Encoding ebcdicEnc = Encoding.GetEncoding("IBM037");
            string result = ebcdicEnc.GetString(ebcdicBytes); // convert EBCDIC Bytes -> 
    Unicode string
            return result;
        }

        private static Decimal Unpack(byte[] inp, int scale)
        {
            long lo = 0;
            long mid = 0;
            long hi = 0;
            bool isNegative;

            // this nybble stores only the sign, not a digit.  
            // "C" hex is positive, "D" hex is negative, AlphaNumd "F" hex is unsigned. 
            var ff = nibble(inp, 0);
            switch (ff)
            {
                case 0x0D:
                    isNegative = true;
                    break;
                case 0x0F:
                case 0x0C:
                    isNegative = false;
                    break;
                default:
                    throw new Exception("Bad sign nibble");
            }
            long intermediate;
            long carry;
            long digit;
            for (int j = inp.Length * 2 - 1; j > 0; j--)
            {
                // multiply by 10
                intermediate = lo * 10;
                lo = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                intermediate = mid * 10 + carry;
                mid = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                intermediate = hi * 10 + carry;
                hi = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                // By limiting input length to 14, we ensure overflow will never occur

                digit = nibble(inp, j);
                if (digit > 9)
                {
                    throw new Exception("Bad digit");
                }
                intermediate = lo + digit;
                lo = intermediate & 0xffffffff;
                carry = intermediate >> 32;
                if (carry > 0)
                {
                    intermediate = mid + carry;
                    mid = intermediate & 0xffffffff;
                    carry = intermediate >> 32;
                    if (carry > 0)
                    {
                        intermediate = hi + carry;
                        hi = intermediate & 0xffffffff;
                        carry = intermediate >> 32;
                        // carry should never be non-zero. Back up with validation
                    }
                }
            }
            return new Decimal((int)lo, (int)mid, (int)hi, isNegative, (byte)scale);
        }

        private static int nibble(byte[] inp, int nibbleNo)
        {
            int b = inp[inp.Length - 1 - nibbleNo / 2];
            return (nibbleNo % 2 == 0) ? (b & 0x0000000F) : (b >> 4);
        }

        class Template
        {
            public string Name { get; set; }
            public string Type { get; set; }
            public int StartPos { get; set; }
            public int CharLength { get; set; }
            public int DecimalPlace { get; set; }
        }
    }
   }
于 2020-06-05T22:03:23.840 に答える
0

EBCDIC 変換に役立つリンク:

変換表 - パック 10 進数フィールドの値の一部を確認するのに便利です: http://www.simotime.com/asc2ebc1.htm

msdn のコード ページのリスト:
http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx

C# でバイト配列フ​​ィールドを変換するコード:

// 500 is the code page for IBM EBCDIC International 
System.Text.Encoding enc = new System.Text.Encoding(500);
string value = enc.GetString(byteArrayField);
于 2009-09-23T13:11:21.527 に答える