1

我有一个包含纯文本和一些压缩文本的文件,例如:

Version 01
Maker SomeCompany

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

微软似乎有一个解决方案,即DeflateStream类,但他们的示例显示了如何在整个文件上使用它,而我不知道如何只在文件的一行上使用它。

到目前为止,我有以下内容:

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/等的任何组合都可以在二进制数据中完全随机出现
  • 您无法读取“字符串行”,因为这表明您正在通过Encoding;运行数据。但由于这不是文本数据,再次:这只会给你无法处理的乱码(读取时它会丢失数据)

现在,这两个问题中的第二个可以通过读取StreamAPI 而不是StreamReaderAPI 来解决,因此您只能读取二进制文件;然后,您需要自己查找行尾,使用 anEncoding来探测您能做的事情(注意,如果您使用的是多/可变字节编码,例如 UTF-8,这并不像听起来那么简单)。

然而,这两个问题中的第一个问题本身是无法解决的。为了可靠地做到这一点,您需要某种二进制帧协议——同样,文本文件中不存在这种协议。看起来该示例使用了“mark”和“endmark” - 同样,从技术上讲,这些可能会随机发生,但对于 99.999% 的情况,您可能会侥幸逃脱。Stream那么,诀窍是使用and手动读取整个文件Encoding,寻找“标记”和“结束标记” - 并从压缩数据位中剥离编码为文本的位。然后通过正确的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 回答