2011-02-24 5 views
2

옛날 사람의 손 :-)숫자가 엉망이 아닌 비표준 코드 페이지로 EBCDIC 데이터를 읽는 방법?

메인 프레임 DB2 테이블에서 바이너리 덤프를 읽습니다. 이 테이블에는 varchar, char, smallint, integer 및 float 열이 있습니다. 흥미롭게 만들기 위해 DB2는 코드 페이지 424 (히브리어)를 사용합니다. 코드를 독립적으로 사용하려면 코드가 필요합니다.

그래서 나는과 같이 System.Text.Encoding을 사용하여 위해 StreamReader로 파일을 엽니 다

Dim encoding As System.Text.Encoding = System.Text.Encoding.GetEncoding(20424) 
Dim sr As New StreamReader(item.Key, encoding) 

및 사용하여 문자 배열에 자신의 길이에 따라 VARCHAR와 CHAR 데이터를 읽을 진행

sr.ReadBlock(buffer, 0, iFieldBufferSize) 

VARCHAR 열의 처음 2 바이트는 항상 을 버리고 올바른 문자열을

SringValue = encoding.GetString(encoding.GetBytes(buffer)) 
과 함께 가져와야합니다.

그리고 모두 훌륭합니다!

하지만 이제 SMALLINT 열에 도착하고 문제가 있습니다. 서명 된 숫자의 값은 2 바이트로 저장되고 빅 엔디안이기 때문에 내가 수행합니다.

Dim buffer(iFieldBufferSize - 1) As Byte 
buffer(1) = sr.Read ''switch the bytes around! 
buffer(0) = sr.Read 
Dim byteBuffer(iFieldBufferSize - 1) As Byte 
Dim i16 As Int16 = BitConverter.ToUInt16(buffer, 0) 

잘못된 숫자가 표시됩니다. 예를 들어, 바이트가 00 03이라면 버퍼 (1)에서는 0, 버퍼 (0)에서는 3이됩니다. 하지만 두 바이트가 00 20 일 때, 나는 128 개의 버퍼를 버퍼 (0)에 넣는다!

그래서 머리카락을 당기는 반나절 후, 나는 streamreader 선언에서 인코더를 떨어 뜨린다. 이제 버퍼가 32가되어야한다.

결론적으로, 비표준 코드 페이지 인코더는 바이트 판독 값을 엉망으로 만듭니다 !!!

이 문제를 해결하는 방법에 대해 알고 싶습니다.

답변

3

EBCDIC 파일 덤프와 같은 것을 스트림으로 읽을 수 없습니다. StreamReader 클래스는 TextReader 유형이며 을 읽는 데 사용됩니다. 레코드 - 혼합 된 바이너리와 텍스트가 포함 된 복잡한 데이터 구조입니다.

FileStream을 사용하여 읽기 작업을 수행하고 필요에 따라 옥텟 블록을 읽어야합니다. 다음과 같은 간단한 도우미 메소드가 필요합니다.

private byte[] ReadOctets(Stream input , int size) 
{ 
    if (size < 0) throw new ArgumentOutOfRangeException() ; 

    byte[] octets  = new byte[size] ; 
    int octets_read = input.Read(octets , 0 , size) ; 

    if (octets_read != size) throw new InvalidDataException() ; 

    return octets ; 
} 

public string readCharVarying(Stream input) 
{ 
    short size  = readShort(input) ; 

    return readCharFixed(input , size) ; 
} 

public string readCharFixed(Stream input , int size) 
{ 
    Encoding e   = System.Text.Encoding.GetEncoding(20424) ; 
    byte[] octets  = ReadOctets(input , size) ; 
    string value  = e.GetString(octets) ; 

    return value ; 
} 

private short readShort(Stream input) 
{ 
    byte[] octets   = ReadOctets(input,2) ; 
    short bigEndianValue = BitConverter.ToInt16(octets,0) ; 
    short littleEndianValue = System.Net.IPAddress.NetworkToHostOrder(bigEndianValue) ; 

    return littleEndianValue ; 
} 

private int readInt(Stream input) 
{ 
    byte[] octets   = ReadOctets(input,4) ; 
    int bigEndianValue = BitConverter.ToInt32(octets,0) ; 
    int littleEndianValue = System.Net.IPAddress.NetworkToHostOrder(bigEndianValue) ; 

    return littleEndianValue ; 
} 

private long readLong(Stream input) 
{ 
    byte[] octets   = ReadOctets(input,8) ; 
    long bigEndianValue = BitConverter.ToInt64(octets,0) ; 
    long littleEndianValue = System.Net.IPAddress.NetworkToHostOrder(bigEndianValue) ; 

    return littleEndianValue ; 
} 

IBM 메인 프레임은 일반적으로 파일 시스템에 고정 길이 또는 가변 길이 레코드를 가지고 있습니다. 고정 길이는 쉽습니다. 레코드 길이를 알아야하고 Read() 메서드를 한 번 호출하여 레코드의 모든 바이트를 읽고 필요에 따라 조각을 변환 할 수 있습니다.

가변 길이 레코드는 2 옥텟 (16 비트) 논리 레코드 길이와 2 옥텟 (16 비트) 0 값으로 구성된 4- 옥텟 레코드 설명자 워드로 시작하는 것이 조금 까다 롭습니다. 논리적 레코드 길이는 4- 옥텟 레코드 서술자 워드와 배타적이다.

가변 스팬 레코드가 표시 될 수도 있습니다. 이들은 4 옥텟 접두사가 세그먼트 설명자 단어라는 점을 제외하고는 가변 길이 레코드와 유사합니다. 처음 2 옥텟은 세그먼트 길이를 포함하고, 다음 옥텟은 세그먼트 유형을 식별하고 마지막 옥텟은 NUL (0x00)이다. 다음과 같이 세그먼트 유형은 다음과 같습니다

  • × 00는
  • 0x01로이 스팬 기록
  • 은 0x10의 첫 번째 세그먼트가 있음을 나타냅니다 완전한 논리 레코드를 표시이 스팬 레코드의 마지막 세그먼트가 있음을 나타냅니다
  • 0x11은이 레코드가 스팬 레코드의 "내부"세그먼트, 즉 "첫 번째 또는 마지막 세그먼트가 아닌 여러 세그먼트 레코드의 세그먼트"임을 나타냅니다.

가변 길이와 가변 스팬 레코드를 동일하게 처리 할 수 ​​있습니다. 이 중 하나를 읽으려면 먼저 세그먼트/레코드/설명자 단어를 구문 분석하고 전체 레코드를 구성 세그먼트의 바이트 []로 읽고 어셈블 한 다음 해당 바이트를 변환하기 위해 수행해야 할 작업을 수행해야합니다 []을 (를) 사용할 수있는 양식으로 만드십시오.

+0

Nicholas, 놀라 울 정도로 도움이되었습니다! FLOAT에 대한 도우미 메서드를 추가 할 정도로 친절 할 수 있습니까? FLOAT (53) 컬럼이 여러 개 있습니다. – GilShalit

+0

플로트는 다소 어렵습니다. IBM 메인 프레임은 IEEE 754를 사용하지 않습니다. 이들은 IEEE 754 이전의 기본 16 기반 부동 소수점 형식을 사용합니다. Microsoft는 http://support.microsoft.com/kb/235856에서 일부 코드가있는 KB 기사를 가지고 있습니다. 또한 IBM의 [i] Principles of Operation [/ i]을보십시오. http://www.hack.org/mc/texts/principles-of-operation.pdf에서 구 버전을 얻고 http://www-01.ibm.com/support/docview에서 IBM의 최신 버전을 구하십시오. .wss? uid = isg2b9de5f05a9d57819852571c500428f9a (하지만 IBM에 등록해야합니다). –

+0

니클라스, 내일은 이것들을 보겠습니다 ... 고맙습니다! – GilShalit

3

Do 이 파일을 읽으려면 StreamReader를 사용하십시오. 파일의 이진수를 문자 인 것처럼 해석하여 값을 엉망으로 만들 것입니다. FileStream과 BinaryReader를 사용하십시오. 만 문자열을 나타내는 파일에서 바이트 그룹을 변환 할 때 Encoding.GetString()을 사용하십시오.

+0

감사합니다. 나를 올바른 방향으로 가리켰다. 나는 EBCDIC 파일에 관한 질문이 게시 된 지 몇 분 만에 답변 될 것이라고 꿈을 꾸었다! – GilShalit

2

@ 한자 Passant가 정확합니다. 디스크립션에서 알 수 있듯이 바이너리 데이터가 포함 된 파일을 읽는 경우 텍스트 인 것처럼 파일을 읽는 것은 올바르지 않습니다.

BinaryReader 클래스에는 문자 인코딩을 매개 변수 중 하나로 사용하는 생성자가 포함되어 있습니다. 이를 사용하여 텍스트가 아닌 (2 진) 부분의 해석에 영향을주지 않고 파일의 히브리어 EBCDIC 문자열을 일반 유니 코드 문자열로 자동 변환 할 수 있습니다.

또한 문자열을 그냥 버리는 대신 2 바이트 VARCHAR 길이 필드를 사용해야합니다.

파일이 .NET BinaryWriter 클래스로 인코딩되지 않았으므로이 경우 ReadString() 메서드가 작동하지 않습니다. 대신 VARCHAR (또는 CHAR 필드의 하드 코딩 된 길이) 길이를 가져와 ReadChars (int) 메서드에 전달해야합니다. 그런 다음 반환 된 문자 배열에서 결과 문자열을 구성합니다.