2013-01-09 4 views
3

ECDIC에서 ASCII 형식으로 파일을 변환하려고하는데 흥미로운 문제가 발생했습니다. 파일에는 부호가있는 2 진 정수 (레코드 레이아웃에서 B4로 설명 됨) 및 고정밀 숫자 값 (레코드 레이아웃에서 L8로 설명 됨)이있는 고정 길이 레코드가 들어 있습니다. 문제없이 문자 데이터를 변환 할 수 있었지만이 숫자 값을 변환하는 방법을 모르겠습니다. 원래 시스템의 참조 설명서 (IBM 5110)에서 필드는 아래에 설명되어 있습니다.EBCDIC에서 ASCII 값으로 변환, 숫자 값 처리

B는 기본 내부 데이터 포맷으로 변환하여야한다 고정 소수점 부호 이진 정수 형식의 숫자 데이터 항목의 길이 (2, 4 또는 8 바이트)를 나타낸다. 레코드 I/O 파일 입력의 경우 레코드의 다음 2, 4 또는 8 바이트는 인 부호있는 이진 값을 시스템에서 내부 데이터 형식으로 변환하고 READ에 지정된 변수 에 할당됩니다 FORM 문을 사용하는 FILE 또는 REREAD FILE 문

L은 긴 수치 정밀도 (8 문자)를 나타낸다. 입력의 경우이 항목은 레코드의 8 위치, 고정 소수점 값이 READ FILE 또는 REREAD FILE 문에 지정된 에 해당하는 숫자 변수로 변환없이 할당되어야 함을 나타냅니다.

편집 : 여기에 먼저 고정 레코드 크기를 알아야합니다 내가 변환

private void ConvertFile(EbcdicFile file) 
{ 
    if (file == null) return; 

    var filePath = Path.Combine(file.Path, file.FileName); 
    if (!File.Exists(filePath)) 
    { 
     this.Logger.Info(string.Format("Cannot convert file {0}. It does not exist.", filePath)); 
     return; 
    } 

    var ebcdic = Encoding.GetEncoding(37); 
    string convertedFilepath = Path.Combine(file.Path, file.ConvertedFileName); 
    byte[] fileData = File.ReadAllBytes(filePath); 

    if (!file.HasNumericFields) 
     File.WriteAllBytes(convertedFilepath, Encoding.Convert(ebcdic, Encoding.ASCII, fileData)); 
    else 
    { 
     var convertedFileData = new List<byte>(); 
     for (int position = 0; position < fileData.Length; position += file.RecordLength) 
     { 
      var segment = new ArraySegment<byte>(fileData, position, file.RecordLength); 
      file.Fields.ForEach(field => 
       { 
        var fieldSegment = segment.Array.Skip(segment.Offset + field.Start - 1).Take(field.Length); 
        if (field.Type.Equals("string", StringComparison.OrdinalIgnoreCase)) 
        { 
         convertedFileData.AddRange(
          Encoding.Convert(ebcdic, Encoding.ASCII, fieldSegment.ToArray()) 
          ); 
        } 
        else if (field.Type.Equals("B4", StringComparison.OrdinalIgnoreCase)) 
        { 
         // Not sure how to convert this field 
        } 
        else if (field.Type.Equals("L8", StringComparison.OrdinalIgnoreCase)) 
        { 
         // Not sure how to convert this field 
        } 
       }); 
     } 

     File.WriteAllBytes(convertedFilepath, convertedFileData.ToArray()); 
    } 
} 
+0

코드를 표시 할 수 있습니까? –

+0

현재 상황을 설명했지만 질문을하지 않았습니다 :-). 난 당신이 "나는 IBM 5110 BASIC에 의해 기록 된 데이터 파일의 B4 및 L8 필드 형식을 변환 어떻게"말하고 싶은 생각 .... 당신은 우리에게 몇 가지 예를 들어 데이터 (16 진수 덤프), 바람직하게는 또한 올바른 해석을 표시해야합니다. – Ben

답변

1

좋아, 그래서 두 필드를 변환하는 방법을 알아 냈습니다. B4 필드는 매우 간단합니다. 본질적으로 정수로 변환 할 수있는 4 바이트 배열입니다.

//The IBM 5110 were big endian machines, so reverse the array 
if (BitConverter.IsLittleEndian) 
    Array.Reverse(by); 

int value = BitConverter.ToInt32(by, 0); 

L8 필드

IBM Double Precision Float를 나타내는 8 바이트로 배열된다. 이것을 IEEE 754 Float으로 변환하는 많은 방법이 있습니다. 여기에 내가 기사의지도에 따라 사용되는 버전의

: 몇 가지 예에서 확인할 수 있습니다.

private double IbmFloatToDouble(byte[] value) 
{ 
    if (ReferenceEquals(null, value)) 
     throw new ArgumentNullException("value"); 

    if (BitConverter.ToInt64(value, 0) == 0) 
     return 0; 

    int exponentBias = 64; 
    int ibmBase = 16; 
    double sign = 0.0D; 

    int signValue = (value[0] & 0x80) >> 7; 
    int exponentValue = (value[0] & 0x7f); 
    double fraction1 = (value[1] << 16) + (value[2] << 8) + value[3]; 
    double fraction2 = (value[4] << 24) + (value[5] << 16) + (value[6] << 8) + value[7]; 
    double exponent24 = 16777216.0;    // 2^24 
    double exponent56 = 72057594037927936.0; // 2^56 

    double mantissa1 = fraction1/exponent24; 
    double mantissa2 = fraction2/exponent56; 
    double mantissa = mantissa1 + mantissa2; 
    double exponent = Math.Pow(ibmBase, exponentValue - exponentBias); 

    if (signValue == 0) 
     sign = 1.0; 
    else 
     sign = -1.0; 

    return (sign * mantissa * exponent); 
} 
2

을 위해 사용하고 코드입니다. FileStream.Read()를 사용하여 바이트 수의 한 레코드를 읽습니다. 그런 다음 Encoding.GetString()을 사용하여 문자열로 변환합니다.

그런 다음 String.SubString()을 사용하여 필드를 기록에서 제거합니다. B4는 단순히 길이가 4이고 길이가 L8 인 SubString 호출입니다. Decimal.Parse()를 사용하여 이러한 필드를 숫자로 변환하십시오. 결과를 나눠야 할 수도 있습니다. 고정 소수점 승수가 사용 된 것은 분명하지 않습니다. 좋은 확률은 100입니다.

+0

의견에 감사드립니다. 당신이 묘사 한 것은 내가 그것을 구현 한 방법이라고 믿습니다. 하지만 각 레코드를 바이트 배열로 처리하고 있습니다. Decimal.Parse를 고정 소수점 승수와 함께 사용하는 방법을 살펴 보겠습니다. 그것은 좋은 제안입니다. 불행히도 레코드 레이아웃과 출력을위한 문서 만 있으므로 시행 착오의 종류입니다. – Garett