1

圧縮されたテキストと混合されたプレーンテキストを含むファイルがあります。たとえば、次のようになります。

Version 01
Maker SomeCompany

l 73
mark
h�22V0P���w�/�+Q0���L)�66□ // This line was compressed using DeflateZLib
endmark

マイクロソフトにはDeflateStreamクラスという解決策があるようですが、その例はファイル全体でそれを使用する方法を示していますが、ファイルの1行だけで使用する方法はわかりません.

これまでのところ、次のものがあります。

bool isDeflate = false;

using (var fs = new FileStream(@"C:\Temp\MyFile.dat", FileMode.Open)
using (var reader = new StreamReader(fs))
{
     string line;
     while ((line = reader.ReadLine()) != null)
     {
         if (isDeflate)
         {
             if (line == "endmark")
             {
                 isDeflate = false;
             }
             else
             {
                 line = DeflateSomehow(line);
             }
         }

         if (line == "mark")
         {
             isDeflate = true;
         }

         Console.WriteLine(line);
     }
}

public string DeflateSomehow(string line)
{
    // How do I deflate just that string?
}

ファイルは私が作成したものではないため (ファイルを読み込むだけです)、その構造を制御することはできません... しかし、私は現在持っているコードに縛られていません。単にDeflateSomehowメソッドを実装する方法を理解するだけでなく、それ以上の変更が必要な場合は、それで問題ありません。

4

1 に答える 1

3

デフレート ストリームは、バイナリ データに対して機能します。テキスト ファイルの途中にある任意のバイナリ チャンクは、破損したテキスト ファイルとも呼ばれます。これを正しくデコードする方法はありません:

  • バイナリデータについて話すときの「行」の定義がないため、「行」を読むことはできません。CR/LF/CRLF/etc の任意の組み合わせは、バイナリ データで完全にランダムに発生する可能性があります。
  • 「文字列行」を読み取ることはできません。これは、データをEncoding;で実行していることを示唆しているためです。しかし、これはテキストデータではないため、繰り返しますが、処理できない意味不明なものになります(読み取り時にデータが失われます)

さて、これら 2 つの問題の 2 つ目は、StreamAPI ではなく APIを介して読み取ることで解決できるため、バイナリStreamReaderのみを読み取ることになります。次に、 を使用してできることを調べて、自分で行末を探す必要があります(UTF-8 などのマルチ/可変バイト エンコーディングを使用している場合、これは思ったほど単純ではないことに注意してください)。Encoding

ただし、これら 2 つの問題のうち最初の 1 つは、本質的にそれ自体では解決できません。これを確実に行うには、ある種のバイナリ フレーミング プロトコルが必要になります。これも、テキスト ファイルには存在しません。この例では "mark" と "endmark" を使用しているように見えます - 繰り返しますが、技術的にはこれらがランダムに発生する可能性がありますが、99.999% の場合はおそらくそれで済むでしょう。Streamトリックは、 andを使用してファイル全体を手動で読み取り、Encoding「マーク」と「エンドマーク」を探し、圧縮データであるビットからテキストとしてエンコードされたビットを取り除くことです。次に、encoded-as-text ピースを正しいEncoding.

でも!バイナリを読み取っている時点では、それは簡単です: 適切な量を (データが書き込まれるフレーミング/センチネル プロトコルを使用して) バッファリングし、次のようなものを使用します。

using(var ms = new MemoryStream(bytes))
using(var inflate = new GZipStream(ms, CompressionMode.Decompress))
{
    // now read from 'inflate'
}

マーカーの追加と、l 73それが ASCII であるという情報により、もう少し実行可能になります。

ここのSOのデータはすでに破損しているため(テキストとしてバイナリを投稿するとそれが行われます)、これはにはうまくいきませんが、基本的には次のようなものです:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Text.RegularExpressions;
class Program
{
    static void Main()
    {
        using (var file = File.OpenRead("my.txt"))
        using (var buffer = new MemoryStream())
        {
            List<string> lines = new List<string>();
            string line;
            while ((line = ReadToCRLF(file, buffer)) != null)
            {
                lines.Add(line);
                Console.WriteLine(line);
                if (line == "mark" && lines.Count >= 2)
                {
                    var match = Regex.Match(lines[lines.Count - 2], "^l ([0-9]+)$");
                    int bytes;
                    if (match.Success && int.TryParse(match.Groups[1].Value, out bytes))
                    {
                        ReadBytes(file, buffer, bytes);
                        string inflated = Inflate(buffer);
                        lines.Add(inflated); // or something similar
                        Console.WriteLine(inflated);
                    }
                }
            }
        }

    }
    static string Inflate(Stream source)
    {
        using (var deflate = new DeflateStream(source, CompressionMode.Decompress, true))
        using (var reader = new StreamReader(deflate, Encoding.ASCII))
        {
            return reader.ReadToEnd();
        }
    }
    static void ReadBytes(Stream source, MemoryStream buffer, int count)
    {
        buffer.SetLength(count);
        int read, offset = 0;
        while (count > 0 && (read = source.Read(buffer.GetBuffer(), offset, count)) > 0)
        {
            count -= read;
            offset += read;
        }
        if (count != 0) throw new EndOfStreamException();
        buffer.Position = 0;
    }
    static string ReadToCRLF(Stream source, MemoryStream buffer)
    {
        buffer.SetLength(0);
        int next;
        bool wasCr = false;
        while ((next = source.ReadByte()) >= 0)
        {
            if(next == 10 && wasCr) { // CRLF
                // end of line (minus the CR)
                return Encoding.ASCII.GetString(
                     buffer.GetBuffer(), 0, (int)buffer.Length - 1);
            }
            buffer.WriteByte((byte)next);
            wasCr = next == 13;
        }
        // end of file
        if (buffer.Length == 0) return null;
        return Encoding.ASCII.GetString(buffer.GetBuffer(), 0, (int)buffer.Length);

    }
}
于 2013-08-21T13:36:52.317 に答える