是否有使用 VB.NET 解压缩 .Z 文件的库?

发布于 2024-10-09 09:20:39 字数 242 浏览 0 评论 0原文

我需要解压缩 .Z 文件列表,这些文件将保存在使用 Visual Basic.NET 的文件夹中。 例如,假设有一个类似 C:\FilesToBeUnzipped 的文件夹。该文件夹内有 5 至 6 个扩展名为 .Z 的文件。我需要解压缩所有这些 .Z 文件并将解压缩的文件保存在 C:\UnzippedDataFiles 等文件夹中。

这在 VB.NET 中可能吗?有没有免费的组件或类库可以实现它?

I need to unzip a list of .Z files which will be kept in a folder using Visual Basic.NET.
For example, consider that there is a folder like C:\FilesToBeUnzipped. Inside this folder there will some 5 to 6 files with .Z as extension. I need to unzip all these .Z files and save the unzipped files in a folder like C:\UnzippedDataFiles.

Is this possible in VB.NET? Is there any free component or class library to achive it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

古镇旧梦 2024-10-16 09:20:39

我也遇到了这个问题,只需要解压缩一个 *.Z 文件

请注意,我不是这个问题的原始作者,但我想提供堆栈溢出的源代码副本,以防万一原来的网站不再可用。

可以在此处找到原始帖子

这是一个如何使用的示例

using System;
using System.IO;

using Ebixio.LZW;

class MainClass
{
    public static void Main(string[] args)
    {
        byte[] buffer = new byte[4096];
        string outFile = Path.GetFileNameWithoutExtension(args[0]);

        using (Stream inStream = new LzwInputStream(File.OpenRead(args[0])))
        using (FileStream outStream = File.Create(outFile)) {
            int read;
            while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
                outStream.Write(buffer, 0, read);
            }
        }
    }
}

这是我制作的作为静态方法的修改版本。它允许选择 *.Z 文件位置和提取到的路径:

public static void LzwUncompress(this string path, string unzippedPath) {
            byte[] buffer = new byte[4096];
            //string outFile = Path.GetFileNameWithoutExtension(path);

            using (Stream inStream = new Ebixio.LZW.LzwInputStream(File.OpenRead(path)))
            using (FileStream outStream = File.Create(unzippedPath)) {
                int read;
                while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
                    outStream.Write(buffer, 0, read);
                }
            }
        }

使用静态方法的示例:

savePath.LzwUncompress(unzippedPath);

将此类文件添加到您的项目

// LzwInputStream.cs
//
// Copyright (C) 2009 Gabriel Burca
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//

using System;
using System.IO;

namespace Ebixio.LZW {

    /// <summary>
    /// This class contains constants used for LZW
    /// </summary>
    sealed public class LzwConstants {
        /// <summary>
        /// Magic number found at start of LZW header: 0x1f 0x9d
        /// </summary>
        public const int MAGIC = 0x1f9d;

        /// <summary>
        /// Maximum number of bits per code
        /// </summary>
        public const int MAX_BITS = 16;

        /* 3rd header byte:
         * bit 0..4 Number of compression bits
         * bit 5    Extended header
         * bit 6    Free
         * bit 7    Block mode
         */

        /// <summary>
        /// Mask for 'number of compression bits'
        /// </summary>
        public const int BIT_MASK = 0x1f;

        /// <summary>
        /// Indicates the presence of a fourth header byte
        /// </summary>
        public const int EXTENDED_MASK = 0x20;
        //public const int FREE_MASK      = 0x40;

        /// <summary>
        /// Reserved bits
        /// </summary>
        public const int RESERVED_MASK = 0x60;

        /// <summary>
        /// Block compression: if table is full and compression rate is dropping,
        /// clear the dictionary.
        /// </summary>
        public const int BLOCK_MODE_MASK = 0x80;

        /// <summary>
        /// LZW file header size (in bytes)
        /// </summary>
        public const int HDR_SIZE = 3;

        /// <summary>
        /// Initial number of bits per code
        /// </summary>
        public const int INIT_BITS = 9;

        LzwConstants() {
        }
    }


    /// <summary>
    /// This filter stream is used to decompress a LZW format stream.
    /// Specifically, a stream that uses the LZC compression method.
    /// This file format is usually associated with the .Z file extension.
    ///
    /// See http://en.wikipedia.org/wiki/Compress
    /// See http://wiki.wxwidgets.org/Development:_Z_File_Format
    ///
    /// The file header consists of 3 (or optionally 4) bytes. The first two bytes
    /// contain the magic marker "0x1f 0x9d", followed by a byte of flags.
    ///
    /// Based on Java code by Ronald Tschalar, which in turn was based on the unlzw.c
    /// code in the gzip package.
    /// </summary>
    /// <example> This sample shows how to unzip a compressed file
    /// <code>
    /// using System;
    /// using System.IO;
    ///
    /// using Ebixio.LZW;
    ///
    /// class MainClass
    /// {
    ///     public static void Main(string[] args)
    ///     {
    ///         byte[] buffer = new byte[4096];
    ///         using (Stream inStream = new LzwInputStream(File.OpenRead(args[0])))
    ///         using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) {
    ///             int read;
    ///             while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
    ///                 outStream.Write(buffer, 0, read);
    ///             }
    ///         }
    ///     }
    /// }
    /// </code>
    /// </example>
    public class LzwInputStream : Stream {
        byte[] one = new byte[1];
        bool headerParsed = false;

        // string table stuff
        private const int TBL_CLEAR = 0x100;
        private const int TBL_FIRST = TBL_CLEAR + 1;

        private int[] tabPrefix;
        private byte[] tabSuffix;
        private int[] zeros = new int[256];
        private byte[] stack;

        // various state
        private bool blockMode;
        private int nBits;
        private int maxBits;
        private int maxMaxCode;
        private int maxCode;
        private int bitMask;
        private int oldCode;
        private byte finChar;
        private int stackP;
        private int freeEnt;

        // input buffer
        private byte[] data = new byte[10000];
        private int bitPos = 0, end = 0, got = 0;
        private bool eof = false;
        private const int EXTRA = 64;

        /// <summary>
        /// Creates a LzwInputStream
        /// </summary>
        /// <param name="baseInputStream">
        /// The stream to read compressed data from (baseInputStream LZW format)
        /// </param>
        public LzwInputStream(Stream baseInputStream) {
            this.baseInputStream = baseInputStream;
        }

        /// <summary>
        /// See <see cref="System.IO.Stream.ReadByte"/>
        /// </summary>
        /// <returns></returns>
        public override int ReadByte() {
            int b = Read(one, 0, 1);
            if (b == 1)
                return (one[0] & 0xff);
            else
                return -1;
        }

        /// <summary>
        /// Moves the unread data in the buffer to the beginning and resets
        /// the pointers.
        /// </summary>
        /// <param name="bitPos"></param>
        /// <returns></returns>
        private int ResetBuf(int bitPos) {
            int pos = bitPos >> 3;
            Array.Copy(data, pos, data, 0, end - pos);
            end -= pos;
            return 0;
        }


        private void Fill() {
            got = baseInputStream.Read(data, end, data.Length - 1 - end);
            if (got > 0) end += got;
        }


        private void ParseHeader() {
            headerParsed = true;

            byte[] hdr = new byte[LzwConstants.HDR_SIZE];

            int result = baseInputStream.Read(hdr, 0, hdr.Length);

            // Check the magic marker
            if (result < 0) throw new ApplicationException("Failed to read LZW header");
            if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff)) {
                throw new ApplicationException(String.Format(
                    "Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}",
                    hdr[0], hdr[1]));
            }

            // Check the 3rd header byte
            blockMode = (hdr[2] & LzwConstants.BLOCK_MODE_MASK) > 0;
            maxBits = hdr[2] & LzwConstants.BIT_MASK;

            if (maxBits > LzwConstants.MAX_BITS) {
                throw new ApplicationException("Stream compressed with " + maxBits +
                    " bits, but decompression can only handle " +
                    LzwConstants.MAX_BITS + " bits.");
            }

            if ((hdr[2] & LzwConstants.RESERVED_MASK) > 0) {
                throw new ApplicationException("Unsupported bits set in the header.");
            }

            // Initialize variables
            maxMaxCode = 1 << maxBits;
            nBits = LzwConstants.INIT_BITS;
            maxCode = (1 << nBits) - 1;
            bitMask = maxCode;
            oldCode = -1;
            finChar = 0;
            freeEnt = blockMode ? TBL_FIRST : 256;

            tabPrefix = new int[1 << maxBits];
            tabSuffix = new byte[1 << maxBits];
            stack = new byte[1 << maxBits];
            stackP = stack.Length;

            for (int idx = 255; idx >= 0; idx--)
                tabSuffix[idx] = (byte)idx;
        }

        #region Stream Overrides

        /// <summary>
        /// Reads decompressed data into the provided buffer byte array
        /// </summary>
        /// <param name ="buffer">
        /// The array to read and decompress data into
        /// </param>
        /// <param name ="offset">
        /// The offset indicating where the data should be placed
        /// </param>
        /// <param name ="count">
        /// The number of bytes to decompress
        /// </param>
        /// <returns>The number of bytes read. Zero signals the end of stream</returns>
        public override int Read(byte[] buffer, int offset, int count) {
            if (!headerParsed) ParseHeader();

            if (eof) return -1;
            int start = offset;

            /* Using local copies of various variables speeds things up by as
             * much as 30% in Java! Performance not tested in C#.
             */
            int[] lTabPrefix = tabPrefix;
            byte[] lTabSuffix = tabSuffix;
            byte[] lStack = stack;
            int lNBits = nBits;
            int lMaxCode = maxCode;
            int lMaxMaxCode = maxMaxCode;
            int lBitMask = bitMask;
            int lOldCode = oldCode;
            byte lFinChar = finChar;
            int lStackP = stackP;
            int lFreeEnt = freeEnt;
            byte[] lData = data;
            int lBitPos = bitPos;


            // empty stack if stuff still left
            int sSize = lStack.Length - lStackP;
            if (sSize > 0) {
                int num = (sSize >= count) ? count : sSize;
                Array.Copy(lStack, lStackP, buffer, offset, num);
                offset += num;
                count -= num;
                lStackP += num;
            }

            if (count == 0) {
                stackP = lStackP;
                return offset - start;
            }


            // loop, filling local buffer until enough data has been decompressed
        MainLoop: do {
                if (end < EXTRA) Fill();

                int bitIn = (got > 0) ? (end - end % lNBits) << 3 :
                                        (end << 3) - (lNBits - 1);

                while (lBitPos < bitIn) {
                    #region A
                    // handle 1-byte reads correctly
                    if (count == 0) {
                        nBits = lNBits;
                        maxCode = lMaxCode;
                        maxMaxCode = lMaxMaxCode;
                        bitMask = lBitMask;
                        oldCode = lOldCode;
                        finChar = lFinChar;
                        stackP = lStackP;
                        freeEnt = lFreeEnt;
                        bitPos = lBitPos;

                        return offset - start;
                    }

                    // check for code-width expansion
                    if (lFreeEnt > lMaxCode) {
                        int nBytes = lNBits << 3;
                        lBitPos = (lBitPos - 1) +
                        nBytes - (lBitPos - 1 + nBytes) % nBytes;

                        lNBits++;
                        lMaxCode = (lNBits == maxBits) ? lMaxMaxCode :
                                                        (1 << lNBits) - 1;

                        lBitMask = (1 << lNBits) - 1;
                        lBitPos = ResetBuf(lBitPos);
                        goto MainLoop;
                    }
                    #endregion

                    #region B
                    // read next code
                    int pos = lBitPos >> 3;
                    int code = (((lData[pos] & 0xFF) |
                        ((lData[pos + 1] & 0xFF) << 8) |
                        ((lData[pos + 2] & 0xFF) << 16)) >>
                        (lBitPos & 0x7)) & lBitMask;

                    lBitPos += lNBits;

                    // handle first iteration
                    if (lOldCode == -1) {
                        if (code >= 256) throw new ApplicationException("corrupt input: " + code + " > 255");

                        lFinChar = (byte)(lOldCode = code);
                        buffer[offset++] = lFinChar;
                        count--;
                        continue;
                    }

                    // handle CLEAR code
                    if (code == TBL_CLEAR && blockMode) {
                        Array.Copy(zeros, 0, lTabPrefix, 0, zeros.Length);
                        lFreeEnt = TBL_FIRST - 1;

                        int nBytes = lNBits << 3;
                        lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes;
                        lNBits = LzwConstants.INIT_BITS;
                        lMaxCode = (1 << lNBits) - 1;
                        lBitMask = lMaxCode;

                        // Code tables reset

                        lBitPos = ResetBuf(lBitPos);
                        goto MainLoop;
                    }
                    #endregion

                    #region C
                    // setup
                    int inCode = code;
                    lStackP = lStack.Length;

                    // Handle KwK case
                    if (code >= lFreeEnt) {
                        if (code > lFreeEnt) {
                            throw new ApplicationException("corrupt input: code=" + code +
                                ", freeEnt=" + lFreeEnt);
                        }

                        lStack[--lStackP] = lFinChar;
                        code = lOldCode;
                    }

                    // Generate output characters in reverse order
                    while (code >= 256) {
                        lStack[--lStackP] = lTabSuffix[code];
                        code = lTabPrefix[code];
                    }

                    lFinChar = lTabSuffix[code];
                    buffer[offset++] = lFinChar;
                    count--;

                    // And put them out in forward order
                    sSize = lStack.Length - lStackP;
                    int num = (sSize >= count) ? count : sSize;
                    Array.Copy(lStack, lStackP, buffer, offset, num);
                    offset += num;
                    count -= num;
                    lStackP += num;
                    #endregion

                    #region D
                    // generate new entry in table
                    if (lFreeEnt < lMaxMaxCode) {
                        lTabPrefix[lFreeEnt] = lOldCode;
                        lTabSuffix[lFreeEnt] = lFinChar;
                        lFreeEnt++;
                    }

                    // Remember previous code
                    lOldCode = inCode;

                    // if output buffer full, then return
                    if (count == 0) {
                        nBits = lNBits;
                        maxCode = lMaxCode;
                        bitMask = lBitMask;
                        oldCode = lOldCode;
                        finChar = lFinChar;
                        stackP = lStackP;
                        freeEnt = lFreeEnt;
                        bitPos = lBitPos;

                        return offset - start;
                    }
                    #endregion
                }   // while

                lBitPos = ResetBuf(lBitPos);

            } while (got > 0);  // do..while

            nBits = lNBits;
            maxCode = lMaxCode;
            bitMask = lBitMask;
            oldCode = lOldCode;
            finChar = lFinChar;
            stackP = lStackP;
            freeEnt = lFreeEnt;
            bitPos = lBitPos;

            eof = true;
            return offset - start;
        }


        /// <summary>
        /// Gets a value indicating whether the current stream supports reading
        /// </summary>
        public override bool CanRead {
            get {
                return baseInputStream.CanRead;
            }
        }

        /// <summary>
        /// Gets a value of false indicating seeking is not supported for this stream.
        /// </summary>
        public override bool CanSeek {
            get {
                return false;
            }
        }

        /// <summary>
        /// Gets a value of false indicating that this stream is not writeable.
        /// </summary>
        public override bool CanWrite {
            get {
                return false;
            }
        }

        /// <summary>
        /// A value representing the length of the stream in bytes.
        /// Always throws a NotSupportedException when attempting to get the length.
        /// </summary>
        /// <exception cref="NotSupportedException">Attempting to get the length</exception>
        public override long Length {
            get {
                throw new NotSupportedException("LzwInputStream Length not supported");
            }
        }

        /// <summary>
        /// The current position within the stream.
        /// Throws a NotSupportedException when attempting to set the position.
        /// </summary>
        /// <exception cref="NotSupportedException">Attempting to set the position</exception>
        public override long Position {
            get {
                return baseInputStream.Position;
            }
            set {
                throw new NotSupportedException("LzwInputStream Position not supported");
            }
        }

        /// <summary>
        /// Flushes the baseInputStream
        /// </summary>
        public override void Flush() {
            baseInputStream.Flush();
        }

        /// <summary>
        /// Sets the position within the current stream
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="offset">The relative offset to seek to.</param>
        /// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
        /// <returns>The new position in the stream.</returns>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override long Seek(long offset, SeekOrigin origin) {
            throw new NotSupportedException("LzwInputStream Seek not supported");
        }

        /// <summary>
        /// Set the length of the current stream
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="value">The new length value for the stream.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void SetLength(long value) {
            throw new NotSupportedException("LzwInputStream SetLength not supported");
        }

        /// <summary>
        /// Writes a sequence of bytes to stream and advances the current position
        /// This method always throws a NotSupportedException
        /// </summary>
        /// <param name="buffer">Thew buffer containing data to write.</param>
        /// <param name="offset">The offset of the first byte to write.</param>
        /// <param name="count">The number of bytes to write.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void Write(byte[] buffer, int offset, int count) {
            throw new NotSupportedException("LzwInputStream Write not supported");
        }

        /// <summary>
        /// Writes one byte to the current stream and advances the current position
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="value">The byte to write.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void WriteByte(byte value) {
            throw new NotSupportedException("LzwInputStream WriteByte not supported");
        }

        /// <summary>
        /// Entry point to begin an asynchronous write.  Always throws a NotSupportedException.
        /// </summary>
        /// <param name="buffer">The buffer to write data from</param>
        /// <param name="offset">Offset of first byte to write</param>
        /// <param name="count">The maximum number of bytes to write</param>
        /// <param name="callback">The method to be called when the asynchronous write operation is completed</param>
        /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests</param>
        /// <returns>An <see cref="System.IAsyncResult">IAsyncResult</see> that references the asynchronous write</returns>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
            throw new NotSupportedException("LzwInputStream BeginWrite not supported");
        }

        /// <summary>
        /// Closes the input stream.  When <see cref="IsStreamOwner"></see>
        /// is true the underlying stream is also closed.
        /// </summary>
        public override void Close() {
            if (!isClosed) {
                isClosed = true;
                if (isStreamOwner) {
                    baseInputStream.Close();
                }
            }
        }

        #endregion


        /// <summary>
        /// Get/set flag indicating ownership of underlying stream.
        /// When the flag is true <see cref="Close"/> will close the underlying stream also.
        /// </summary>
        /// <remarks>
        /// The default value is true.
        /// </remarks>
        public bool IsStreamOwner {
            get { return isStreamOwner; }
            set { isStreamOwner = value; }
        }

        /// <summary>
        /// Base stream the inflater reads from.
        /// </summary>
        protected Stream baseInputStream;

        /// <summary>
        /// Flag indicating wether this instance has been closed or not.
        /// </summary>
        bool isClosed;

        /// <summary>
        /// Flag indicating wether this instance is designated the stream owner.
        /// When closing if this flag is true the underlying stream is closed.
        /// </summary>
        bool isStreamOwner = true;
    }
}

I also ran into this issue as well, just needed to decompress a *.Z file

Please note, I am not the original author of this however, wanted to provide copy of source on stack overflow in case the original site is no longer available.

The original post can be found here

Here is an example of how to use the class:

using System;
using System.IO;

using Ebixio.LZW;

class MainClass
{
    public static void Main(string[] args)
    {
        byte[] buffer = new byte[4096];
        string outFile = Path.GetFileNameWithoutExtension(args[0]);

        using (Stream inStream = new LzwInputStream(File.OpenRead(args[0])))
        using (FileStream outStream = File.Create(outFile)) {
            int read;
            while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
                outStream.Write(buffer, 0, read);
            }
        }
    }
}

Here is a modifed version of this as a static method that i made. It allows choosing the *.Z file location and a path to extract to:

public static void LzwUncompress(this string path, string unzippedPath) {
            byte[] buffer = new byte[4096];
            //string outFile = Path.GetFileNameWithoutExtension(path);

            using (Stream inStream = new Ebixio.LZW.LzwInputStream(File.OpenRead(path)))
            using (FileStream outStream = File.Create(unzippedPath)) {
                int read;
                while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
                    outStream.Write(buffer, 0, read);
                }
            }
        }

An example using the static method:

savePath.LzwUncompress(unzippedPath);

Add this class file to your project:

// LzwInputStream.cs
//
// Copyright (C) 2009 Gabriel Burca
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//

using System;
using System.IO;

namespace Ebixio.LZW {

    /// <summary>
    /// This class contains constants used for LZW
    /// </summary>
    sealed public class LzwConstants {
        /// <summary>
        /// Magic number found at start of LZW header: 0x1f 0x9d
        /// </summary>
        public const int MAGIC = 0x1f9d;

        /// <summary>
        /// Maximum number of bits per code
        /// </summary>
        public const int MAX_BITS = 16;

        /* 3rd header byte:
         * bit 0..4 Number of compression bits
         * bit 5    Extended header
         * bit 6    Free
         * bit 7    Block mode
         */

        /// <summary>
        /// Mask for 'number of compression bits'
        /// </summary>
        public const int BIT_MASK = 0x1f;

        /// <summary>
        /// Indicates the presence of a fourth header byte
        /// </summary>
        public const int EXTENDED_MASK = 0x20;
        //public const int FREE_MASK      = 0x40;

        /// <summary>
        /// Reserved bits
        /// </summary>
        public const int RESERVED_MASK = 0x60;

        /// <summary>
        /// Block compression: if table is full and compression rate is dropping,
        /// clear the dictionary.
        /// </summary>
        public const int BLOCK_MODE_MASK = 0x80;

        /// <summary>
        /// LZW file header size (in bytes)
        /// </summary>
        public const int HDR_SIZE = 3;

        /// <summary>
        /// Initial number of bits per code
        /// </summary>
        public const int INIT_BITS = 9;

        LzwConstants() {
        }
    }


    /// <summary>
    /// This filter stream is used to decompress a LZW format stream.
    /// Specifically, a stream that uses the LZC compression method.
    /// This file format is usually associated with the .Z file extension.
    ///
    /// See http://en.wikipedia.org/wiki/Compress
    /// See http://wiki.wxwidgets.org/Development:_Z_File_Format
    ///
    /// The file header consists of 3 (or optionally 4) bytes. The first two bytes
    /// contain the magic marker "0x1f 0x9d", followed by a byte of flags.
    ///
    /// Based on Java code by Ronald Tschalar, which in turn was based on the unlzw.c
    /// code in the gzip package.
    /// </summary>
    /// <example> This sample shows how to unzip a compressed file
    /// <code>
    /// using System;
    /// using System.IO;
    ///
    /// using Ebixio.LZW;
    ///
    /// class MainClass
    /// {
    ///     public static void Main(string[] args)
    ///     {
    ///         byte[] buffer = new byte[4096];
    ///         using (Stream inStream = new LzwInputStream(File.OpenRead(args[0])))
    ///         using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) {
    ///             int read;
    ///             while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0) {
    ///                 outStream.Write(buffer, 0, read);
    ///             }
    ///         }
    ///     }
    /// }
    /// </code>
    /// </example>
    public class LzwInputStream : Stream {
        byte[] one = new byte[1];
        bool headerParsed = false;

        // string table stuff
        private const int TBL_CLEAR = 0x100;
        private const int TBL_FIRST = TBL_CLEAR + 1;

        private int[] tabPrefix;
        private byte[] tabSuffix;
        private int[] zeros = new int[256];
        private byte[] stack;

        // various state
        private bool blockMode;
        private int nBits;
        private int maxBits;
        private int maxMaxCode;
        private int maxCode;
        private int bitMask;
        private int oldCode;
        private byte finChar;
        private int stackP;
        private int freeEnt;

        // input buffer
        private byte[] data = new byte[10000];
        private int bitPos = 0, end = 0, got = 0;
        private bool eof = false;
        private const int EXTRA = 64;

        /// <summary>
        /// Creates a LzwInputStream
        /// </summary>
        /// <param name="baseInputStream">
        /// The stream to read compressed data from (baseInputStream LZW format)
        /// </param>
        public LzwInputStream(Stream baseInputStream) {
            this.baseInputStream = baseInputStream;
        }

        /// <summary>
        /// See <see cref="System.IO.Stream.ReadByte"/>
        /// </summary>
        /// <returns></returns>
        public override int ReadByte() {
            int b = Read(one, 0, 1);
            if (b == 1)
                return (one[0] & 0xff);
            else
                return -1;
        }

        /// <summary>
        /// Moves the unread data in the buffer to the beginning and resets
        /// the pointers.
        /// </summary>
        /// <param name="bitPos"></param>
        /// <returns></returns>
        private int ResetBuf(int bitPos) {
            int pos = bitPos >> 3;
            Array.Copy(data, pos, data, 0, end - pos);
            end -= pos;
            return 0;
        }


        private void Fill() {
            got = baseInputStream.Read(data, end, data.Length - 1 - end);
            if (got > 0) end += got;
        }


        private void ParseHeader() {
            headerParsed = true;

            byte[] hdr = new byte[LzwConstants.HDR_SIZE];

            int result = baseInputStream.Read(hdr, 0, hdr.Length);

            // Check the magic marker
            if (result < 0) throw new ApplicationException("Failed to read LZW header");
            if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff)) {
                throw new ApplicationException(String.Format(
                    "Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}",
                    hdr[0], hdr[1]));
            }

            // Check the 3rd header byte
            blockMode = (hdr[2] & LzwConstants.BLOCK_MODE_MASK) > 0;
            maxBits = hdr[2] & LzwConstants.BIT_MASK;

            if (maxBits > LzwConstants.MAX_BITS) {
                throw new ApplicationException("Stream compressed with " + maxBits +
                    " bits, but decompression can only handle " +
                    LzwConstants.MAX_BITS + " bits.");
            }

            if ((hdr[2] & LzwConstants.RESERVED_MASK) > 0) {
                throw new ApplicationException("Unsupported bits set in the header.");
            }

            // Initialize variables
            maxMaxCode = 1 << maxBits;
            nBits = LzwConstants.INIT_BITS;
            maxCode = (1 << nBits) - 1;
            bitMask = maxCode;
            oldCode = -1;
            finChar = 0;
            freeEnt = blockMode ? TBL_FIRST : 256;

            tabPrefix = new int[1 << maxBits];
            tabSuffix = new byte[1 << maxBits];
            stack = new byte[1 << maxBits];
            stackP = stack.Length;

            for (int idx = 255; idx >= 0; idx--)
                tabSuffix[idx] = (byte)idx;
        }

        #region Stream Overrides

        /// <summary>
        /// Reads decompressed data into the provided buffer byte array
        /// </summary>
        /// <param name ="buffer">
        /// The array to read and decompress data into
        /// </param>
        /// <param name ="offset">
        /// The offset indicating where the data should be placed
        /// </param>
        /// <param name ="count">
        /// The number of bytes to decompress
        /// </param>
        /// <returns>The number of bytes read. Zero signals the end of stream</returns>
        public override int Read(byte[] buffer, int offset, int count) {
            if (!headerParsed) ParseHeader();

            if (eof) return -1;
            int start = offset;

            /* Using local copies of various variables speeds things up by as
             * much as 30% in Java! Performance not tested in C#.
             */
            int[] lTabPrefix = tabPrefix;
            byte[] lTabSuffix = tabSuffix;
            byte[] lStack = stack;
            int lNBits = nBits;
            int lMaxCode = maxCode;
            int lMaxMaxCode = maxMaxCode;
            int lBitMask = bitMask;
            int lOldCode = oldCode;
            byte lFinChar = finChar;
            int lStackP = stackP;
            int lFreeEnt = freeEnt;
            byte[] lData = data;
            int lBitPos = bitPos;


            // empty stack if stuff still left
            int sSize = lStack.Length - lStackP;
            if (sSize > 0) {
                int num = (sSize >= count) ? count : sSize;
                Array.Copy(lStack, lStackP, buffer, offset, num);
                offset += num;
                count -= num;
                lStackP += num;
            }

            if (count == 0) {
                stackP = lStackP;
                return offset - start;
            }


            // loop, filling local buffer until enough data has been decompressed
        MainLoop: do {
                if (end < EXTRA) Fill();

                int bitIn = (got > 0) ? (end - end % lNBits) << 3 :
                                        (end << 3) - (lNBits - 1);

                while (lBitPos < bitIn) {
                    #region A
                    // handle 1-byte reads correctly
                    if (count == 0) {
                        nBits = lNBits;
                        maxCode = lMaxCode;
                        maxMaxCode = lMaxMaxCode;
                        bitMask = lBitMask;
                        oldCode = lOldCode;
                        finChar = lFinChar;
                        stackP = lStackP;
                        freeEnt = lFreeEnt;
                        bitPos = lBitPos;

                        return offset - start;
                    }

                    // check for code-width expansion
                    if (lFreeEnt > lMaxCode) {
                        int nBytes = lNBits << 3;
                        lBitPos = (lBitPos - 1) +
                        nBytes - (lBitPos - 1 + nBytes) % nBytes;

                        lNBits++;
                        lMaxCode = (lNBits == maxBits) ? lMaxMaxCode :
                                                        (1 << lNBits) - 1;

                        lBitMask = (1 << lNBits) - 1;
                        lBitPos = ResetBuf(lBitPos);
                        goto MainLoop;
                    }
                    #endregion

                    #region B
                    // read next code
                    int pos = lBitPos >> 3;
                    int code = (((lData[pos] & 0xFF) |
                        ((lData[pos + 1] & 0xFF) << 8) |
                        ((lData[pos + 2] & 0xFF) << 16)) >>
                        (lBitPos & 0x7)) & lBitMask;

                    lBitPos += lNBits;

                    // handle first iteration
                    if (lOldCode == -1) {
                        if (code >= 256) throw new ApplicationException("corrupt input: " + code + " > 255");

                        lFinChar = (byte)(lOldCode = code);
                        buffer[offset++] = lFinChar;
                        count--;
                        continue;
                    }

                    // handle CLEAR code
                    if (code == TBL_CLEAR && blockMode) {
                        Array.Copy(zeros, 0, lTabPrefix, 0, zeros.Length);
                        lFreeEnt = TBL_FIRST - 1;

                        int nBytes = lNBits << 3;
                        lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes;
                        lNBits = LzwConstants.INIT_BITS;
                        lMaxCode = (1 << lNBits) - 1;
                        lBitMask = lMaxCode;

                        // Code tables reset

                        lBitPos = ResetBuf(lBitPos);
                        goto MainLoop;
                    }
                    #endregion

                    #region C
                    // setup
                    int inCode = code;
                    lStackP = lStack.Length;

                    // Handle KwK case
                    if (code >= lFreeEnt) {
                        if (code > lFreeEnt) {
                            throw new ApplicationException("corrupt input: code=" + code +
                                ", freeEnt=" + lFreeEnt);
                        }

                        lStack[--lStackP] = lFinChar;
                        code = lOldCode;
                    }

                    // Generate output characters in reverse order
                    while (code >= 256) {
                        lStack[--lStackP] = lTabSuffix[code];
                        code = lTabPrefix[code];
                    }

                    lFinChar = lTabSuffix[code];
                    buffer[offset++] = lFinChar;
                    count--;

                    // And put them out in forward order
                    sSize = lStack.Length - lStackP;
                    int num = (sSize >= count) ? count : sSize;
                    Array.Copy(lStack, lStackP, buffer, offset, num);
                    offset += num;
                    count -= num;
                    lStackP += num;
                    #endregion

                    #region D
                    // generate new entry in table
                    if (lFreeEnt < lMaxMaxCode) {
                        lTabPrefix[lFreeEnt] = lOldCode;
                        lTabSuffix[lFreeEnt] = lFinChar;
                        lFreeEnt++;
                    }

                    // Remember previous code
                    lOldCode = inCode;

                    // if output buffer full, then return
                    if (count == 0) {
                        nBits = lNBits;
                        maxCode = lMaxCode;
                        bitMask = lBitMask;
                        oldCode = lOldCode;
                        finChar = lFinChar;
                        stackP = lStackP;
                        freeEnt = lFreeEnt;
                        bitPos = lBitPos;

                        return offset - start;
                    }
                    #endregion
                }   // while

                lBitPos = ResetBuf(lBitPos);

            } while (got > 0);  // do..while

            nBits = lNBits;
            maxCode = lMaxCode;
            bitMask = lBitMask;
            oldCode = lOldCode;
            finChar = lFinChar;
            stackP = lStackP;
            freeEnt = lFreeEnt;
            bitPos = lBitPos;

            eof = true;
            return offset - start;
        }


        /// <summary>
        /// Gets a value indicating whether the current stream supports reading
        /// </summary>
        public override bool CanRead {
            get {
                return baseInputStream.CanRead;
            }
        }

        /// <summary>
        /// Gets a value of false indicating seeking is not supported for this stream.
        /// </summary>
        public override bool CanSeek {
            get {
                return false;
            }
        }

        /// <summary>
        /// Gets a value of false indicating that this stream is not writeable.
        /// </summary>
        public override bool CanWrite {
            get {
                return false;
            }
        }

        /// <summary>
        /// A value representing the length of the stream in bytes.
        /// Always throws a NotSupportedException when attempting to get the length.
        /// </summary>
        /// <exception cref="NotSupportedException">Attempting to get the length</exception>
        public override long Length {
            get {
                throw new NotSupportedException("LzwInputStream Length not supported");
            }
        }

        /// <summary>
        /// The current position within the stream.
        /// Throws a NotSupportedException when attempting to set the position.
        /// </summary>
        /// <exception cref="NotSupportedException">Attempting to set the position</exception>
        public override long Position {
            get {
                return baseInputStream.Position;
            }
            set {
                throw new NotSupportedException("LzwInputStream Position not supported");
            }
        }

        /// <summary>
        /// Flushes the baseInputStream
        /// </summary>
        public override void Flush() {
            baseInputStream.Flush();
        }

        /// <summary>
        /// Sets the position within the current stream
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="offset">The relative offset to seek to.</param>
        /// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
        /// <returns>The new position in the stream.</returns>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override long Seek(long offset, SeekOrigin origin) {
            throw new NotSupportedException("LzwInputStream Seek not supported");
        }

        /// <summary>
        /// Set the length of the current stream
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="value">The new length value for the stream.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void SetLength(long value) {
            throw new NotSupportedException("LzwInputStream SetLength not supported");
        }

        /// <summary>
        /// Writes a sequence of bytes to stream and advances the current position
        /// This method always throws a NotSupportedException
        /// </summary>
        /// <param name="buffer">Thew buffer containing data to write.</param>
        /// <param name="offset">The offset of the first byte to write.</param>
        /// <param name="count">The number of bytes to write.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void Write(byte[] buffer, int offset, int count) {
            throw new NotSupportedException("LzwInputStream Write not supported");
        }

        /// <summary>
        /// Writes one byte to the current stream and advances the current position
        /// Always throws a NotSupportedException
        /// </summary>
        /// <param name="value">The byte to write.</param>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override void WriteByte(byte value) {
            throw new NotSupportedException("LzwInputStream WriteByte not supported");
        }

        /// <summary>
        /// Entry point to begin an asynchronous write.  Always throws a NotSupportedException.
        /// </summary>
        /// <param name="buffer">The buffer to write data from</param>
        /// <param name="offset">Offset of first byte to write</param>
        /// <param name="count">The maximum number of bytes to write</param>
        /// <param name="callback">The method to be called when the asynchronous write operation is completed</param>
        /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests</param>
        /// <returns>An <see cref="System.IAsyncResult">IAsyncResult</see> that references the asynchronous write</returns>
        /// <exception cref="NotSupportedException">Any access</exception>
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) {
            throw new NotSupportedException("LzwInputStream BeginWrite not supported");
        }

        /// <summary>
        /// Closes the input stream.  When <see cref="IsStreamOwner"></see>
        /// is true the underlying stream is also closed.
        /// </summary>
        public override void Close() {
            if (!isClosed) {
                isClosed = true;
                if (isStreamOwner) {
                    baseInputStream.Close();
                }
            }
        }

        #endregion


        /// <summary>
        /// Get/set flag indicating ownership of underlying stream.
        /// When the flag is true <see cref="Close"/> will close the underlying stream also.
        /// </summary>
        /// <remarks>
        /// The default value is true.
        /// </remarks>
        public bool IsStreamOwner {
            get { return isStreamOwner; }
            set { isStreamOwner = value; }
        }

        /// <summary>
        /// Base stream the inflater reads from.
        /// </summary>
        protected Stream baseInputStream;

        /// <summary>
        /// Flag indicating wether this instance has been closed or not.
        /// </summary>
        bool isClosed;

        /// <summary>
        /// Flag indicating wether this instance is designated the stream owner.
        /// When closing if this flag is true the underlying stream is closed.
        /// </summary>
        bool isStreamOwner = true;
    }
}
伊面 2024-10-16 09:20:39

SevenZipSharp怎么样?

它是一个围绕 7-Zip 压缩/解压缩引擎,在 LGPL.尽管代码是用 C# 编写的,但该库提供的所有功能都可以在 VB.NET 代码中完全访问。

如果您喜欢自己动手,请查看 7-Zip SDK

我看到 SharpZipLibDotNetZip推荐了很多,但我不确定他们是否支持.Z档案。
我知道 7-Zip 可以,更不用说它是我最喜欢的开源应用程序之一。

How about SevenZipSharp?

It's a managed wrapper around the 7-Zip compression/decompression engine that is released under the LGPL. Although the code is written in C#, all of the functions provided by the library are fully accessible in your VB.NET code.

If you're a do-it-yourselfer, check out the 7-Zip SDK.

I see SharpZipLib and DotNetZip recommended a lot, but I am not sure if they support .Z archives.
I know that 7-Zip does, not to mention it's one of my favorite open source apps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文