2

背景: 設計が不十分な EBCDIC ファイルに ASCII 行ターミネータを使用するバイナリ データを使用するアプリケーションを作成する必要があり、そのバイナリ データに ASCII CRLF が含まれていることがあり、行が正しく分割されないことがあります。この古いファイル形式を使用して、各レコードの最後に CRLF をドロップする必要があります。

エンコーディングで aStreamReaderを使用すると、メソッドが期待どおりではなく行末としてのみ読み取られるようになるため、返されるすべての文字列 (最初の文字列以降) は LF ( ASCII) で始まります。IBM037ReadLine()\r\r\nReadLine0A

問題を再現するサンプル プログラム:

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

class Program
{
  static void Main(string[] args)
  {
    //generate example EBCDIC data
    List<byte> bytes = new List<byte>();
    Encoding EBCDIC = Encoding.GetEncoding("IBM037");
    bytes.AddRange(Encoding.Convert(Encoding.ASCII, EBCDIC, Encoding.ASCII.GetBytes("Some nice ascii text")));
    bytes.AddRange(new byte[] { (byte)'\r', (byte)'\n' });
    bytes.AddRange(Encoding.Convert(Encoding.ASCII, EBCDIC, Encoding.ASCII.GetBytes("Some more nice ascii text")));

    //read it using StreamReader
    using(MemoryStream ms = new MemoryStream(bytes.ToArray()))
    using (StreamReader reader = new StreamReader(ms, EBCDIC))
    {
      string line = string.Empty;
      while ((line = reader.ReadLine()) != null)
      {
        EBCDIC.GetBytes(line).ToList().ForEach(c => Console.Write(c));
        Console.WriteLine();
      }
    }
    Console.ReadLine();
  }
}

出力は次のようになります。

226150148133641491371311336412916213113713764163133167163
1022615014813364148150153133641491371311336412916213113713764163133167163

2 行目の先頭にある 10 は存在しないはずです。これは、CRLF シーケンスの LF であるためです。

ReadLineこの方法についての私の理解は次のとおりです。

行は、一連の文字の後にライン フィード ("\n")、キャリッジ リターン ("\r")、またはキャリッジ リターンの直後にライン フィード ("\r\n") が続くものとして定義されます。返される文字列には、終了のキャリッジ リターンまたはライン フィードは含まれません。ソース

エンコーディングがそれを変更することについては何も言わないので、それによると、CRだけでなく、データ内の完全なCRLFを読み取る必要があります。

更新: 私はすでにこの問題を回避し、データを読み取る独自の方法を実装しましたが、私の質問はまだ次のとおりです:ReadLine缶に書かれていることをなぜしなかったのですか?

4

2 に答える 2

2

EBCDIC でエンコードされていることを伝えるストリームに(byte)'\r'andを詰め込みます。(byte)'\n'StreamReader

の値(byte) '\r'は 0x0d で、たまたま ASCII と EBCDIC の両方でキャリッジ リターンになっています。

の値(byte) '\n'は 0x0a で、ASCII では改行ですが、EBCDIC では改行ではありません。

EBCDIC Encoder クラスが値 0x0a を .NET Unicodechar型にデコードする方法を見ると、Unicode の数値charが 142 (または 0x8e) であることがわかります。そして、その文字は改行ではありません。(なぜ142にデコードされるのかわかりません)。

2 行目の先頭に「10」が出力されているのは、そこに改行があるためではなく、値 142 の char が値 10 の EBCDIC バイトに再エンコードされているためです (部分式EBCDIC.GetBytes(line)) 。 .

したがって、質問に簡単に答えると、ReadLine()改行が続く改行ではなく、改行のみが表示されます。

whileループを次のように変更します。

while ((line = reader.ReadLine()) != null)
{
    line.ToList().ForEach(c => { Console.Write(c); Console.Write(" "); });
    Console.WriteLine();
    line.ToList().ForEach(c => { Console.Write(Convert.ToInt32(c)); Console.Write(" "); });
    Console.WriteLine();
    EBCDIC.GetBytes(line).ToList().ForEach(c => { Console.Write(c); Console.Write(" "); });
    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine();
}

2 行目に次の出力が表示されます。この出力には、(EBCDIC から変換された) 行が文字として表示され、それらの文字の Unicode 値が表示され、最後にこれらの文字の値が EBCDIC に変換されて表示されます。

? S o m e   m o r e   n i c e   a s c i i   t e x t
142 83 111 109 101 32 109 111 114 101 32 110 105 99 101 32 97 115 99 105 105 32 116 101 120 116
10 226 150 148 133 64 148 150 153 133 64 149 137 131 133 64 129 162 131 137 137 64 163 133 167 163
于 2015-02-19T08:50:25.967 に答える
2

MSDNフォーラムで次の議論を見つけました。

このドキュメントによると、セクション「EBCDIC lineFeed mappings cause invalid characters」の下部付近に、IBM037 には改行用の 2 つのコード、0x15 と 0x25 があります。.NET は 0x25 を使用しているようです:

byte[] bytes = System.Text.Encoding.GetEncoding("IBM037").GetBytes("hello\r\n");

それを 0x15 にマップした別の Web ページを見ました。ASCIIが勝ったのも不思議ではありません...

Wikipedia von EBCDIC 037 を確認すると、実際にバイト 21 (0x15) が「改行」として定義され、37 (0x25) が「改行」として定義され、バイト 13 (0x0D) が古き良き「キャリッジ リターン」として定義されていることが確認されました。

したがって、ASCII は EBCDIC 037 のサブセットではありません。

次の場合、バイト 0x10 と 0x13 を EBCDIC でエンコードする必要があるバイトに追加するため、テスト コードに欠陥があります。

bytes.AddRange(new byte[] { (byte)'\r', (byte)'\n' });

代わりに次のことを試してください。

bytes.AddRange(Encoding.Convert(Encoding.ASCII, EBCDIC, Encoding.ASCII.GetBytes(
    "Some nice ascii text\r\nSome more nice ascii text")));

"\r\n" は EBCDIC のバイト 13 と 37 に変換されるため、結果のバイトの読み取りは正常に機能します。ReadLine()次に、EBCDIC「改行」であるバイト 37 を正しくスキップします。

これはReadLine() 、 bytes ではなく Unicode 文字を比較するためです。EBCDIC "NewLine" (0x25) バイトは、Unicode 文字 '\n' としてデコードされます。

結論

  1. すべてが正常に機能します。
  2. すべてのエンコーディングにサブセットとして ASCII があるわけではありません。
  3. ReadLine() は Unicode 文字で動作するため、エンコード/デコードの問題である必要があります。
  4. 元の問題の入力データを確認します。無効な (EBCDIC の) 改行文字が含まれている可能性があります。
于 2015-02-19T07:07:38.513 に答える