%Title: Run-length encoding con Ruby
%Author: Andrea Ganduglia
%Date: 2009-06-11 05:36:02
%Copyright: (c)2009 Andrea Ganduglia
Il **Run-lenght encoding** (**RLE**) \`e un tipo di compressione //lossless// piuttosto primitivo, ma comunque efficace con stringhe che contengono lunghe sequenze di byte identici. Ad esempio:
~$ banner -w 20 W > Hello.txt
~$ cat Hello.txt
#
###
#######
#######
##
#####
######
##
### #
#
Il file ''Hello.txt'' contiene una stringa di 167 bytes, formata da solo tre caratteri: ''SPACE'', ''#'', ''NEW LINE'' che compressa in RLE diventa:
18 #
16 3#
12 7#
7 7#
7 ##
9 5#
5 6#
10 ##
14 3# #
18 #
Questa nuova stringa occupa solo 58 bytes ed \`e facilmente decodificabile: si prenda ad esempio la penultima linea: "''14 3# #\n''". L'espressione RLE ci dice che abbiamo 14 caratteri ''SPACE'', 3 caratteri ''#'', 1 carattere ''SPACE'' (letterale), 1 carattere ''#'' (letterale) e 1 carattere ''NEW LINE'' (letterale) [20 bytes espressi con 8 bytes].
Trascrivendo queste indicazioni otteniamo:
01234567890123456789 (caratteri sulla linea)
### #
La stringa RLE \`e stata ottenuta utilizzando il coder Ruby listato poco sotto. Questo coder non converte tutti i caratteri della stringa, ma solo quelli ripetuti almeno 3 volte di seguito, la misura minima perch\`e RLE sia efficace.
{|| Compressioni RLE
{|! Stringa originale | Stringa RLE | Bytes Stringa originale | Bytes Stringa RLE | Spazio occupato
{|XX A |XX 1A |XX 1 |XX 2 |DX +100\%
{|XX AA |XX 2A |XX 2 |XX 2 |DX 0\%
{|XX AAA |XX 3A |XX 3 |XX 2 |DX -33.3\%
{|XX AAAA |XX 4A |XX 4 |XX 2 |DX -50\%
Vedi: {{http://en.wikipedia.org/wiki/Run-length_encoding}}
== Codice ==
Il codice qui sotto funziona abbastanza bene con i file di testo e i file BMP.
#!/usr/bin/ruby
#
# rle_coder (v.0.3) RLE Encoder/Decoder
# (c)2009 Andrea Ganduglia - www.frequenze.it
# GPL Software
# Released under GPL3 - www.gnu.org
def rle_encode(str)
o = []; c = 0; s = nil;
str = str.gsub(/\\([0-9])/,"\\\\\\1").split('')
for i in 0..(str.size-1)
# Fine del ciclo
if c > 0 && str[i] != s
o << "#{c}#{s}"
c = 0; s = nil;
end
# Continuo
if c > 0 && str[i] == s
c = c+1
end
# Inizio il ciclo
if c == 0 && str[i+1] == str[i] && str[i+2] == str[i] && str[i] =~ /\D/
s = str[i]
c = c+1
end
# Niente
if c == 0 && s == nil
if str[i] =~ /\d/
o << "\\#{str[i]}"
else
o << str[i]
end
end
end
# Ultimi caratteri
if c > 0
o << "#{c}#{s}"
end
return o.to_s
end
def rle_decode(str)
o = []
str = str.scan(/\\[0-9]{1}|[0-9]+[A-z\W]{1}|[A-z\W]{1}/)
str.each do |s|
if s =~ /[0-9]+./
i = s.scan(/([0-9]+)(.)/)
o << "#{i[0][1]*i[0][0].to_i}"
elsif s =~ /\\[0-9]{1}/
o << "#{s.gsub(/^\\/,'')}"
else
o << s
end
end
return o.to_s
end
e = "usage: rlt_coder [e|d] [FILE]"
t_file = ARGV[1]
if File.file?(t_file)
f = File.new(t_file).read
else
puts e
exit
end
if ARGV[0] == "d"
e = rle_decode(f)
end
if ARGV[0] == "e"
e = rle_encode(f)
end
print ee
== Un file Bitmap ==
I file BMP sono i migliori da comprimere con questo metodo. Ho creato un file BMP bianco con un punto nero al centro di 100x100px che pesa 20070 bytes, mentre la sua versione RLE pesa solo 263 bytes.
Message-ID: <29835.1244718219@openclose.it>
Mime-Version: 1.0
Subject: RLE di white.bmp
Content-Type: multipart/mixed; boundary="-"
This is a MIME encoded message. Decode it with "munpack"
or any other MIME reading software. Mpack/munpack is available
via anonymous FTP in ftp.andrew.cmu.edu:pub/mpack/
---
Content-Type: application/octet-stream; name="white.rle"
Content-Transfer-Encoding: base64
Content-Disposition: inline; filename="white.rle"
Content-MD5: DQElqmRrExLq9ZYWd9ZkIA==
Qk1mTjYARjMAXDgzAGQzAGQzAAEAEAADMwAgTgAAEwsAABMLMTEA+AAA4AcAAB83ADkwODb/
fe95zvOcEIQQhPOcec597zE4Mv997/OcilIEITQABCGKUvOcfe8xNzj/fe/znIZcMTEyAIZc
MfOcfe8xNzb/ec6KUjE2AIpSec4xNzb/85wEITE2AAQh85wxNzb/EIQyMAAQhDE3Nv8QhDIw
ABCEMTc2//OcBCExNgAEIfOcMTc2/3nOilIxNgCKUnnOMTc2/33v85yGXDExMgCGXDHznH3v
MTc4/33v85yKUgQhNAAEIYpS85x97zE4Mv9973nO85wQhBCE85x5zn3vODY5OP8=
-----
Copiare questo testo in un file ''white.pack'' e fare:
munpach white.pack
si ottiene il file codificato RLE del file ''white.bmp'', quindi:
ruby rle_coder d white.rle > white.bmp
display white.bmp