How to change first 8 characters of every line in every file in a ZipFile

Aug 6, 2011 at 3:32 PM

I have  bunch of text file zipped in zif file.

I wanted to write a routine where every to replace every line starting 8 characters with some input data.I don't wanted to first unzipped copy the file in some folder then change the value and zip it again.Can I change values in the exisiting zipentries directly?

IF yes then please let me know how?

Coordinator
Aug 6, 2011 at 5:37 PM
Edited Aug 6, 2011 at 5:40 PM

You can do what you are imagining, without creating filesystem files, via the ZipInputStream and ZipOutputStream classes. Instantiate a ZipInputStream on the existing zipfile.  Instantiate a ZipOutputStream on a new zipfile.  Iteratively call ZipInputStream.GetNextEntry(), which gives you a readable stream.  Call ZipOutputstream.PutNextEntry().  Read that readable stream, and write the modified output to ZipOutputStream. 

like this:

    int n;
    var buffer = new byte[2048];
    using (var zis = new ZipInputStream(originalZipFile))
    {
        using (var zos = new ZipOutputStream(modifiedZipFile))
        {
            ZipEntry e;
            while ((e = zis.GetNextEntry()) != null)
            {
                zos.PutNextEntry(e.FileName);
                bool firstTime = true;
                while ((n= zis.Read(buffer,0,buffer.Length)) > 0)
                {
                    // edit the first line only.
                    if (firstTime)
                    {
                        // implement your edits here.
                        // the following is just an example. 
                        buffer[0] = 47;  // slash
                        buffer[1] = 47;  // slash
                        buffer[2] = 32;  // space
                        for (int i=3; i < 8; i++)
                            buffer[i] = 65; // 'A'
                        firstTime = false;
                    }
                    zos.Write(buffer,0,n);
                }
            }
        }
    }

This does create a 2nd zip file. You will need to delete the original and rename the modified version back to the original name, after completion of this logic.

 

Aug 6, 2011 at 10:58 PM

I have tried

{
            var objdir = new DirectoryInfo(strPath);
            Cursor.Current = Cursors.WaitCursor;
            int i = 0;
            int n;
            foreach (var di in objdir.GetDirectories())
            {
                foreach (var fi in di.GetFiles())
                {
                    if (fi.Extension != null & fi.Extension.ToLower().EndsWith(".zip"))
                    {
                        try
                        {
                           
                            using (var zis = new ZipInputStream(fi.FullName))
                            {
                                var buffer = new byte[2048];
                                System.Text.Encoding ascii = System.Text.Encoding.ASCII;
                                Byte[] encodedBytes = ascii.GetBytes(MethodToReturnRandomStringof8Characters());
                                i++;
                               
                                var temproraryFileName = "d:\\TestFolder\\" + i.ToString() + ".zip";
                                using (var zos = new ZipOutputStream(temproraryFileName))
                                {
                                    ZipEntry ze;
                                    while ((ze = zis.GetNextEntry()) != null)
                                    {
                                        ze.IsText = true;
                                        zos.PutNextEntry(ze.FileName);
                                        while ((n = zis.Read(buffer, 0, buffer.Length)) > 0)
                                        {

                                            buffer[0] = encodedBytes[0];  
                                            buffer[1] = encodedBytes[1];
                                            buffer[2] = encodedBytes[2]; 
                                            buffer[3] = encodedBytes[3]; 
                                            buffer[4] = encodedBytes[4];  
                                            buffer[5] = encodedBytes[5];  
                                            buffer[6] = encodedBytes[6];  
                                            buffer[7] = encodedBytes[7];

                                            if (ze.FileName.ToLower().EndsWith(".hdr"))
                                            {
                                                buffer[17] = encodedBytes[0]; //also edit some other position
                                                buffer[18] = encodedBytes[1];
                                                buffer[19] = encodedBytes[2];
                                                buffer[20] = encodedBytes[3];
                                                buffer[21] = encodedBytes[4];
                                                buffer[22] = encodedBytes[5];
                                                buffer[23] = encodedBytes[6];
                                                buffer[24] = encodedBytes[7];
                                            }
                                            else
                                            {
                                                buffer[17] = buffer[17]; //same value
                                                buffer[18] = buffer[18]; 
                                                buffer[19] = buffer[19]; 
                                                buffer[20] = buffer[20]; 
                                                buffer[21] = buffer[21]; 
                                                buffer[22] = buffer[22]; 
                                                buffer[23] = buffer[23]; 
                                                buffer[24] = buffer[24]; 
                                            }
                                            zos.Write(buffer, 0, n);
                                        }
                                       
                                    }
                                    zos.Close();
                                    zos.Dispose();
                                    ze = null;
                                    LogInfo(fi.FullName  + " is copied to " + temproraryFileName);
                                }
                                zis.Close();
                                zis.Dispose();
                                buffer = null;
                            }
                        }
                        catch (Exception ex)
                        {
                            LogInfo(ex.Message);
                            continue;

                        }
                    }

                }
            }
            LogInfo(i.ToString() + "files Scanned");
            Cursor.Current = Cursors.Default;
        }

but it didn't worked.What I am seeing is the

Coordinator
Aug 6, 2011 at 11:05 PM

That's wrong - you're changing 8 bytes in every 2048 bytes.  I think you want only the first 8 bytes. Review your code closely, you'll see it.

Also you do not need to Close and Dispose the ZipInputStream and ZipOutputStream if you use a using clause.

 

Aug 6, 2011 at 11:27 PM
Edited Aug 6, 2011 at 11:30 PM

Thanks Cheeso.I know about the Close and dispose doesn't needed in using block but due to general practice I have put that code which will not effect any thing.

Cheezo can you please suggest what will i need to change?

I think var buffer = new byte[8];

will work but it isn't it

Coordinator
Aug 6, 2011 at 11:58 PM

Excuse me for being so direct, but:  In general practice it is bad - harmful - to include useless, purposeless code in any program. 

in your code, you are iterating over all entries in the zip file.  For each entry you are reading data, in chunks of 2048 bytes,.  You read a chunk, then write it, then read, and write, until there is no more to read. If the file is 4096 bytes long, you read exactly 2 chunks.  If the file is 10000 bytes, you read and write 4 complete chunks, and one partial. And for the first chunk only, you told me you want to modify 8 bytes or maybe 16 bytes, or whatever. That is what you told me, but in your code, you are not modifying those bytes for the first chunk only.  You are modifying the first 8 bytes (and some other bytes) of every chunk of 2048.  That means for a file of 10,000 bytes, you introduce modifications into the file 5 times.

You want to modify only the first 8 bytes in the file, you told me. That means you need to make that modification only in the first chunk for each file.  Look at the example I provided earlier, it does the right thing.  Look at the website. http://dotnetzip.codeplex.com/discussions/268031  The code I provided is correct.  Look closely.

 

Aug 7, 2011 at 9:33 AM

Yep I agreed about YAGNI. But due to general practice where i haven't used using block i used to put those close and dispose that's why I have left there.

About my requirement i simply needed to replace starting 8 characters of every line in every zipentry.I have stated about it in my initial post "replace every line starting 8 characters with some input data". Other then that in a file of particular extension (.hdr) i have to replace 8 characters in every line's position 17-24 as well.

Coordinator
Aug 7, 2011 at 2:53 PM

Ah, I missed that . you will need to read line by line then. The StreamReader does that. Wrap it around the ZipInputStream for each entry.

Aug 7, 2011 at 5:52 PM

I know about reading from the StreamReader in Zip entry.But doesn't know how to write in a zip entry

 string strJobID =BLJobReplacer.RandomJobIDGenerator(8);
            using (var zis = new ZipInputStream(originalZipFile))
            {
                using (var zos = new ZipOutputStream(destinationfilename))
                {
                    ZipEntry ze;
                    while ((ze = zis.GetNextEntry()) != null)
                    {
                        zos.PutNextEntry (ze.FileName);
                        using (var buffer = new BufferedStream(ze.OpenReader()))
                        {
                            using (var reader = new StreamReader((buffer), Encoding.ASCII))
                            {
                                string line;
                                while ((line = reader.ReadLine()) != null)
                                {
                                    if (line.Trim().Length > 0)
                                    {
                                       // modify line and write it intothe existing one
                                        //strJobID + line.substring(8, line.length -8));         
                                    }
                                }
                            }
                        }
                    }
                }
            }
also searched for example at http://dotnetzip.codeplex.com/wikipage?title=CS-Examples&referringTitle=Examples  but not able to get for my purpose

Coordinator
Aug 7, 2011 at 9:20 PM

StreamWriter

Aug 8, 2011 at 5:12 AM

I know about StreamWriter.But doesn't know how to use it in this case.

Have used StreamWriter in another location like

 using (StreamWriter sw = File.AppendText(strFileName))
            {
                foreach (String str in GetUpdatedString())
                {
                    sw.WriteLine(str);
                }
                sw.Close();
            }

 

But how to use it in with zos.PutNextEntry (ze.FileName)?

Coordinator
Aug 8, 2011 at 5:27 AM

Wrap the streamwriter around the output stream.  (StreamWriter has a constructor that accepts a stream.)  

also: I believe in this case You cannot use StreamWriter in a using clause.  StreamWriter will close the wrapped stream when it calls Dispose(), which happens implicitly upon exit of a using scope. But you don't want to close the ZipOutputStream (the wrapped stream) , until you've done all entries.  So do not use StreamWriter in a using clause, in this case. 

You also need to be mindful of text encodings.

 

Aug 8, 2011 at 12:15 PM

:) That's what i am not able to done.

zos.PutNextEntry take entry name and doesn't have any method to take Streamwriter to be written

Coordinator
Aug 8, 2011 at 1:24 PM

No, you don't give a StreamWriter to PutNextEntry.  You wrap a streamwriter around the stream.

something like this:

private void ModifyZip()
{
    using (var zis = new ZipInputStream(originalZipFile))
    {
        using (var zos = new ZipOutputStream(destinationfilename))
        {
            ZipEntry ze;
            while ((ze = zis.GetNextEntry()) != null)
            {
                zos.PutNextEntry (ze.FileName);
                var reader = new StreamReader(zis, Encoding.ASCII, false);
                var writer = new StreamWriter(zos, Encoding.ASCII);
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    line = line.Trim();
                    if (line.Length > 1)
                    {
                        ModifyLine(line);
                    }
                    writer.WriteLine(line);
                }
                writer.Flush();
            }
        }
    }
}

I haven't tried this; it's just an idea.

Aug 8, 2011 at 1:37 PM
Edited Aug 12, 2011 at 9:48 AM

Thanks a lot cheeso.

It worked