Как читать данные EBCDIC с нестандартной кодовой страницей, а не путать числа? - PullRequest
2 голосов
/ 24 февраля 2011

Вот один для старых (er) рук: -)

Я читаю двоичный дамп из таблицы DB2 мэйнфреймов. В таблице есть столбцы varchar, char, smallint, integer и float. Чтобы сделать это интересным, DB2 использует кодовую страницу 424 (иврит). Мне нужен мой код, чтобы он не зависел от кодовой страницы.

Итак, я открываю файл с помощью ридера, используя System.Text.Encoding примерно так:

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)

Всегда запоминать первые 2 байта в столбце VARCHAR следует отбрасывать и получить правильную строку с

SringValue = encoding.GetString(encoding.GetBytes(buffer))

И все отлично!

Но теперь я доберусь до МАЛЕНЬКОГО столбца, и у меня проблемы. Значение числа со знаком хранится в 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, я получаю 0 в буфере (1) и 3 в буфере (0) - хорошо. НО, когда два байта 00 20, я получаю 128 чтения в буфер (0)!

Итак, после полдня, потянув меня за волосы, я убрал кодировщик из объявления потокового читателя, и теперь я получаю 32 считывания в буфер (0), как и должно быть !!!

Суть в том, что нестандартный кодировщик кодовых страниц портит показания байтов !!!

Есть идеи как обойти это?

Ответы [ 3 ]

3 голосов
/ 24 февраля 2011

Вы не можете прочитать что-то вроде дампа файла 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 (), а затем преобразовать части по мере необходимости.

Записи переменной длины немного сложнее, они начинаются с 4-октетного слова дескриптора записи, состоящего из 2-октетной (16-битной) длины логической записи, за которой следует 2-октетное (16-битное) значение 0. длина логической записи исключает 4-октетное слово дескриптора записи.

Вы также можете видеть переменные, составные записи. Они похожи на записи переменной длины, за исключением того, что 4-октетный префикс является словом дескриптора сегмента. первые 2 октета содержат длину сегмента, следующий октет идентифицирует тип сегмента, а последний октет равен NUL (0x00). Типы сегментов следующие:

  • 0x00 указывает на полную логическую запись
  • 0x01 указывает, что это первый сегмент составной записи
  • 0x10 указывает, что это последний сегмент составной записи
  • 0x11 указывает, что это «внутренний» сегмент составной записи, то есть «Сегмент многосегментной записи, отличный от первого или последнего сегмента».

Вы можете рассматривать записи переменной длины и переменного диапазона как идентичные. Чтобы прочитать один из них, вам сначала нужно разобрать слово сегмент / запись / дескриптор и прочитать / собрать полную запись в байт [] из составляющих его сегментов, а затем сделать все необходимое для преобразования этого байта. [] в форму, которую вы можете использовать.

3 голосов
/ 24 февраля 2011

Do not используйте StreamReader для чтения этого файла. Это будет интерпретировать двоичные числа в файле, как будто они являются символами, и это испортит их значение. Используйте FileStream и BinaryReader. Только использовать Encoding.GetString (), когда вы переводите группу байтов из файла, представляющего строку.

2 голосов
/ 24 февраля 2011

@ Ганс Пассант прав. Если вы читаете файл, который содержит двоичные данные (как указывает ваше описание), то неправильно читать файл, как если бы он был текстовым.

К счастью, класс BinaryReader включает конструктор, который принимает кодировку символов в качестве одного из параметров. Вы можете использовать это для автоматического преобразования любых строк EBDIC на иврите в файле в обычные строки Unicode без влияния на интерпретацию нетекстовой (двоичной) части.

Кроме того, вам, вероятно, следует использовать двухбайтовое поле длины VARCHAR, чтобы читать ваши строки, а не просто выбрасывать их!

В этом случае метод ReadString () не будет работать, поскольку файл не был закодирован с помощью класса .NET BinaryWriter. Вместо этого вы должны получить длину VARCHAR (или жестко запрограммированную длину поля CHAR) и передать ее в метод ReadChars (int). Затем создайте полученную строку из возвращаемого массива символов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...