:Namespace sfZip ⍝ Version 1.0 December 2014 ⍝ Version 1.1 September 2015 ⍝ UnZipFile updated to open multiple items with decoding (⎕IO ⎕ML ⎕WX)←1 3 3 ∇ r←{nsOrKey}AplToZipFile fileName;case;dataStream;item;key;ns;sfDir;val;zipArchive;⎕USING ⍝ Function to Save and Retrieve to disk a Namespace of only characters. ⍝ The characters will be zipped using the Syncfusion ZipArchive library. ⍝ ⍝ fileName = Fully qualified file name ⍝ nsOrKey = Namespace to save under fileName ⍝ = Key of retrieved Namespace saved under fileName ⍝ = if empty returns the full Namespace saved under fileName ⍝ ⍝ r ← 1 'text' or r ← 1 Namespace ⍝ If successfull ⍝ r ← 0 (error description) ⍝ If failure ⎕USING←0⍴⊂'' ⍝ To Speed up ⎕NC ⍝ Check for 'nsOrKey' and select the case: :If 0=⎕NC'nsOrKey' ⍝ nsOrKey does not exist. Read the fileName and return a NameSpace. case←'ReadFile' :ElseIf 9=⎕NC'nsOrKey' ⍝ nsOrKey is a NameSpace. Save it under FileName. ns←nsOrKey ⋄ case←'SaveNS' :ElseIf ' '=↑1↑0⍴nsOrKey ⍝ nsOrKey is character(s). Read the fileName and return only this Key. key←nsOrKey ⋄ case←'ReturnKey' :Else r←0 'AplToZipFile Error: Left Argument Must be a NameSpace or a Character Vector(Key)' →0 :End ⍝ Required for all the cases. sfDir←'Syncfusion/4.5/' ⍝ Location of the Syncfusion librairies ⎕USING←('Syncfusion.Compression.Zip,',sfDir,'Syncfusion.Compression.Base.dll')'System.IO,mscorlib.dll' 'System,mscorlib.dll' zipArchive←⎕NEW ZipArchive zipArchive.DefaultCompressionLevel←zipArchive.DefaultCompressionLevel.BestSpeed ⍝ AboveNormal BelowNormal Best BestSpeed NoCompression Normal :Select case :Case 'ReadFile' ⍝ Read the file and return a namespace :Trap 0 zipArchive.Open(⊂,fileName) ns←⎕NS'' :For key :In zipArchive.Items.ItemName val←'UTF-8'⎕UCS zipArchive.Item[⊂,key].DataStream.ToArray ⍎'ns.',(¯4↓key),'←val' :End zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL r←1 ns :Else →ERROR :EndTrap :Case 'ReturnKey' ⍝ Return the value of only one Key. :Trap 0 zipArchive.Open(⊂,fileName) :If (⊂key,'.txt')∊zipArchive.Items.ItemName ⍝ key is valid. val←'UTF-8'⎕UCS zipArchive.Item[⊂,key,'.txt'].DataStream.ToArray zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL r←1 val :Else ⍝ key does not exist. r←0 'AplToZipFile: Invalid Key: ',∊key zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL :End :Else →ERROR :EndTrap :Case 'SaveNS' ⍝ Save the Namespace to the file name. :Trap 0 :For key :In ns.⎕NL-2 val←ns.⍎key dataStream←⎕NEW MemoryStream(⊂'UTF-8'⎕UCS val) dataStream.Position←Convert.ToInt64 0 ⍝ Starting Position Set to 0 item←⎕NEW ZipArchiveItem(zipArchive(key,'.txt')dataStream 0 FileAttributes.Archive) {}zipArchive.AddItem(item) :End zipArchive.Save(⊂,fileName) zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL r←1 :Else →ERROR :EndTrap :EndSelect →0 ERROR: ⍝ Show the Error: :If 90=⎕EN r←0('AplToFile EXCEPTION: ',⎕EXCEPTION.Message) :Else r←0((1⊃⎕DM),': ',{(~(∧\' '=⍵)∨(⌽∧\⌽' '=⍵))/⍵}(2⊃⎕DM)) :EndIf ⍝ Try to close zipArchive in case the error happened before closing it: :Trap 0 zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL :EndTrap ∇ ∇ r←ZipFile fileName;sfDir;zipArchive;⎕USING ⍝ Compress a file on disk using Syncfusion ZipArchive library. ⍝ The file will be saved at the same location with a .zip extension. ⍝ ⍝ fileName = fully qualified file name with extension. ⍝ ⍝ r ← 1 ⍝ if successfull ⍝ r ← 0 (error description) ⍝ if failure ⍝ Set ⎕USING for the Syncfusion library. sfDir←'Syncfusion/4.5/' ⍝ Location of the Syncfusion librairies ⎕USING←'Syncfusion.Compression.Zip,',sfDir,'Syncfusion.Compression.Base.dll' :Trap 0 ⍝ Get a ZipArchive object zipArchive←⎕NEW ZipArchive zipArchive.DefaultCompressionLevel←zipArchive.DefaultCompressionLevel.Best ⍝ Add the file to the ZipArchive object {}zipArchive.AddFile(⊂,fileName) ⍝ Change the fileName extension to zip fileName←({⌽(a⍳'.')↓a←⌽⍵}fileName),'.zip' ⍝ Save the zipped file zipArchive.Save(⊂,fileName) ⍝ Clean-up zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL r←1 :Else ⍝ Show the Error: :If 90=⎕EN r←0('ZipFile EXCEPTION: ',⎕EXCEPTION.Message) :Else r←0((1⊃⎕DM),': ',{(~(∧\' '=⍵)∨(⌽∧\⌽' '=⍵))/⍵}(2⊃⎕DM)) :EndIf ⍝ Try to close zipArchive in case the error happened before closing it: :Trap 0 zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL :EndTrap :EndTrap ∇ ∇ r←UnZipFile fileName;item;ns;sfDir;string;zipArchive;⎕USING ⍝ Decompress a file to Apl. Can be used to Unzip an Excel .xlsx or .ods file. ⍝ ⍝ fileName = fully qualified file name with extension. ⍝ ⍝ r ← 1 (namespace) ⍝ if successfull ⍝ r ← 0 (error description) ⍝ if failure ⍝ ⍝ The namespace will have 4 properties: ⍝ Count: Number of item(s) that are unzipped ⍝ ItemName: Name of the item(s) that are unzipped ⍝ Encoding: Encoding detected (UTF-32LE, UTF-32BE, UTF-8 W/Bom, UTF-16LE, UTF-16BE, UTF-8 WO/Bom, ASCII, DEFAULT) ⍝ Data: decoded and unzip data ⍝ Set ⎕USING for the Syncfusion library. sfDir←'Syncfusion/4.5/' ⍝ Location of the Syncfusion librairies ⎕USING←'Syncfusion.Compression.Zip,',sfDir,'Syncfusion.Compression.Base.dll' :Trap 0 ⍝ Get a ZipArchive object zipArchive←⎕NEW ZipArchive ⍝ Open the file with the ZipArchive object zipArchive.Open(⊂,fileName) ⍝ Prepare the nameless namespace ns←⎕NS'' ns.Count←zipArchive.Count :If 0≠ns.Count ⍝ zipArchive is not empty. ns.ItemName←zipArchive.Items.ItemName ns.Data←'' ns.Encoding←'' ⍝ Decode each item. Find the encoding with the function EncodingDetector. :For item :In zipArchive.Items.DataStream.ToArray :Trap 0 string←EncodingDetector item ns.Encoding,←⊂1⊃string ns.Data,←⊂2⊃string :Else ns.Encoding,←⊂'Unknow' ns.Data,←⊂⎕UCS item :EndTrap :EndFor :EndIf ⍝ Clean-up zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL r←1 ns :Else ⍝ Show the Error: :If 90=⎕EN r←0('ZipFile EXCEPTION: ',⎕EXCEPTION.Message) :Else r←0((1⊃⎕DM),': ',{(~(∧\' '=⍵)∨(⌽∧\⌽' '=⍵))/⍵}(2⊃⎕DM)) :EndIf ⍝ Try to close zipArchive in case the error happened before closing it: :Trap 0 zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL :EndTrap :EndTrap ∇ ∇ r←EncodingDetector BA;ENC;string;⎕USING ⍝ Returns The Encoding of a File And it's Content. ⍝ Detected From Their BOM: UTF-32LE, UTF-32BE, UTF-8, UTF-16LE, UTF-16BE. ⍝ An UTF-8 File Without BOM or ASCII Encoding Can Also be Detected. ⍝ ⍝ BA = Byte array (numeric) ⍝ r = (ENCODING NAME) (decoded byte array as characters) ⎕USING←',mscorlib.dll' ⍝ DETECTION WITH THE BYTE ORDER MARK (BOM) :If 255 254 0 0≡4⍴BA ⍝ 0xFF FE 00 00 ⍝ UTF-32LE Detected ENC←⎕NEW System.Text.UTF32Encoding(0 1 1) string←ENC.GetString((⊂,BA),4,¯4+⍴BA) r←'UTF-32LE'string ⋄ →0 :ElseIf 0 0 254 255≡4⍴BA ⍝ 0x00 00 FE FF ⍝ UTF-32BE Detected ENC←⎕NEW System.Text.UTF32Encoding(1 1 1) string←ENC.GetString((⊂,BA),4,¯4+⍴BA) r←'UTF-32BE'string ⋄ →0 :ElseIf 239 187 191≡3⍴BA ⍝ 0xEF BB BF ⍝ UTF-8 Detected ENC←⎕NEW System.Text.UTF8Encoding(1 1) string←ENC.GetString((⊂,BA),3,¯3+⍴BA) r←'UTF-8 W/Bom'string ⋄ →0 :ElseIf 255 254≡2⍴BA ⍝ 0xFF FE ⍝ UTF-16LE Detected ENC←⎕NEW System.Text.UnicodeEncoding(0 1 1) string←ENC.GetString((⊂,BA),2,¯2+⍴BA) r←'UTF-16LE'string ⋄ →0 :ElseIf 254 255≡2⍴BA ⍝ 0xFE FF ⍝ UTF-16BE Detected ENC←⎕NEW System.Text.UnicodeEncoding(1 1 1) string←ENC.GetString((⊂,BA),2,¯2+⍴BA) r←'UTF-16BE'string ⋄ →0 :End ⍝ 0xFE and 0xFF not permitted for UTF-8 →(∨/254 255∊BA)/ASCII ⍝ If there is no BOM the only way to test for UTF-8 is to try to Decode ⍝ and verify if there is an error or not doing so. :Trap 0 string←'UTF-8'⎕UCS BA r←'UTF-8 WO/BOM'string ⋄ →0 :End ASCII: ⍝ *** DECODE TEST FOR ASCII *** :If 1=∧/BA≤127 ⍝ ASCII DETECTED ENC←⎕NEW System.Text.ASCIIEncoding string←ENC.GetString((⊂,BA),0,⍴BA) r←'ASCII'string ⋄ →0 :End ⍝ When All The Other Test Fails, We Use Then The Default Page of The Computer ⍝ This may be incorrect if the file is send from a computer that has a different ⍝ default encoding of the receiving computer. ENC←System.Text.Encoding string←ENC.Default.GetString((⊂,BA),0,⍴BA) r←'DEFAULT'string ∇ ∇ zipString←ZipText string;dataStream;item;outStream;sfDir;zipArchive;⎕USING ⍝ Compress a vector of character using Syncfusion ZipArchive library. ⍝ Set ⎕USING for the Syncfusion dll. sfDir←'Syncfusion/4.5/' ⍝ Location of the Syncfusion librairies ⎕USING←('Syncfusion.Compression.Zip,',sfDir,'Syncfusion.Compression.Base.dll')'System.IO,mscorlib.dll' 'System,mscorlib.dll' ⍝ Convert the APL character vector to a MemoryStream. dataStream←⎕NEW MemoryStream(⊂'UTF-8'⎕UCS string) dataStream.Position←Convert.ToInt64 0 ⍝ Set starting position to 0 ⍝ Get a ZipArchive object zipArchive←⎕NEW ZipArchive zipArchive.DefaultCompressionLevel←zipArchive.DefaultCompressionLevel.Best ⍝ Add a ZipArchiveItem to ZipArchive item←⎕NEW ZipArchiveItem(zipArchive'dir'dataStream 0 FileAttributes.Archive) {}zipArchive.AddItem(item) ⍝ Return the Compress character vector. outStream←⎕NEW MemoryStream zipArchive.Save(outStream 0) zipString←⎕UCS outStream.ToArray ⍝ Clean-up. dataStream.Close ⋄ dataStream.Dispose ⋄ dataStream←⎕NULL outStream.Close ⋄ outStream.Dispose ⋄ outStream←⎕NULL zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL ∇ ∇ string←UnZipText zipString;dataStream;sfDir;zipArchive;⎕USING ⍝ Decompress a string obtained from ZipCompressText ⍝ Set ⎕USING for the Syncfusion dll. sfDir←'Syncfusion/4.5/' ⍝ Location of the Syncfusion librairies ⎕USING←('Syncfusion.Compression.Zip,',sfDir,'Syncfusion.Compression.Base.dll')'System.IO,mscorlib.dll' ⍝ Save the compressed string to a MemoryStream dataStream←⎕NEW MemoryStream(⊂⎕UCS zipString) ⍝ Decompress the MemoryStream and return result. zipArchive←⎕NEW ZipArchive zipArchive.Open(dataStream 0) string←'UTF-8'⎕UCS zipArchive.Item[0].DataStream.ToArray ⍝ Clean-up. dataStream.Close ⋄ dataStream.Dispose ⋄ dataStream←⎕NULL zipArchive.Close ⋄ zipArchive.Dispose ⋄ zipArchive←⎕NULL ∇ :EndNamespace