Base85-Codierung

10

Die Herausforderung

Schreiben Sie ein Programm, das eine einzeilige Zeichenfolge mit beliebigen druckbaren ASCII-Zeichen eingeben und dieselbe in Base85 codierte Zeichenfolge ausgeben kann (unter Verwendung einer Big-Endian-Konvention). Sie können davon ausgehen, dass die Eingabe immer ≤ 100 Zeichen ist.


Eine Anleitung zu Base85

  • Vier Oktette sind in (normalerweise) fünf Base85-Zeichen codiert.

  • Base85-Zeichen reichen von !bis u(ASCII 33 - 117) und z(ASCII 122).

  • Zum Codieren führen Sie kontinuierlich eine Division durch 85 für die vier Oktette (eine 32-Bit-Zahl) durch und addieren 33 zum Rest (nach jeder Division), um das ASCII-Zeichen für den codierten Wert zu erhalten. Beispielsweise erzeugt die erste Anwendung dieses Prozesses das Zeichen ganz rechts im codierten Block.

  • Wenn ein Satz von vier Oktetten nur Null-Bytes enthält, werden sie als zstatt codiert !!!!!.

  • Wenn der letzte Block kürzer als vier Oktette ist, wird er mit Null-Bytes aufgefüllt. Nach dem Codieren wird die gleiche Anzahl von Zeichen, die als Auffüllung hinzugefügt wurden, am Ende der Ausgabe entfernt.

  • Dem codierten Wert sollte vorangestellt <~und gefolgt werden ~>.

  • Der codierte Wert sollte kein Leerzeichen enthalten (für diese Herausforderung).


Beispiele

In: easy
Out: <~ARTY*~>

In: test
Out: <~FCfN8~>

In: code golf
Out: <~@rGmh+D5V/Ac~>

In: Programming Puzzles
Out: <~:i^JeEa`g%Bl7Q+:j%)1Ch7Y~>

Das folgende Snippet codiert eine bestimmte Eingabe in Base85.

Zach Gates
quelle
3
Ich bin verwirrt darüber, warum Sie, da Sie die Eingabe auf druckbares ASCII beschränken, Byte als Synonym für Oktett verwenden und keine 7-Bit-Bytes zulassen.
Peter Taylor
Endianness sollte angegeben werden. Ein Block [0,1,2,3] wird in eine 32-Bit-Zahl als 0x0123 oder 0x3210 konvertiert.
edc65
@ Edc65 Big Endian nach dem Wikipedia-Link
Level River St
3
@ Steveverrill danke. Das sollte im Challenge-Text und nicht in einem externen Link stehen. Zumindest ist es jetzt in einem Kommentar
edc65
Wenn die Eingabe nur druckbare Zeichen enthalten kann, wie kann sie dann vier Nullbytes enthalten?
Luis Mendo

Antworten:

9

CJam, 43 39 35 Bytes

"<~"q4/{:N4Ue]256b85b'!f+}/N,)<"~>"

Probieren Sie es online im CJam-Interpreter aus .

Wie es funktioniert

"<~"      e# Push that string.
q4/       e# Read all input from STDIN and split it into chunks of length 4.
{         e# For each chunk:
  :N      e#   Save it in N.
  4Ue]    e#   Right-pad it with 0's to a length of 4.
  256b85b e#   Convert from base 256 to base 85.
  '!f+    e#   Add '!' to each base-85 digit.
}/        e#
N,)       e# Push the length of the last unpadded chunk, plus 1.
<         e# Keep that many chars of the last encoded chunk.
"~>"      e# Push that string.

Wenn die Eingabe leer war, N,)wird sie auf die Zeichenfolge angewendet "<~". Da Nanfänglich ein einzelnes Zeichen enthalten ist, ist die Ausgabe korrekt.

Wir müssen uns nicht mit z befassen oder die codierten Chunks auf Länge 5 auffüllen, da die Eingabe nur druckbare ASCII-Zeichen enthält.

Dennis
quelle
3
Diese Lösung ähnelt verdächtig der Base85-Version einer ASCII-Zeichenfolge (siehe letztes fragliches Beispiel). Warten Sie ...
ojdo
1
@odjo: Es gibt einige ungültige Zeichen im CJam-Code, der nächste, den ich habe, ist dieser CJam-Interpreter-Link
Schnaader
@ojdo, weil die Herausforderung genau das ist:a program that can take an input of a single-line string containing any ASCII printable characters,...
edc65
5

Python 3, 71 Bytes

from base64 import*
print(a85encode(input().encode(),adobe=1).decode())

Ich habe noch nie in Python Golf gespielt, daher ist dies wahrscheinlich nicht optimal.

Vielen Dank an @ZachGates für das Golfen von 3 Bytes!

Dennis
quelle
1
Sie können input().encode()statt str.encode(input())3 Bytes speichern.
Zach Gates
@ ZachGates Danke! All das En- / Decodieren bringt mich immer noch um.
Dennis
2

Python 2, 193 162 Bytes

from struct import*
i=raw_input()
k=4-len(i)%4&3
i+='\0'*k
o=''
while i:
 b,=unpack('>I',i[-4:]);i=i[:-4]
 while b:o+=chr(b%85+33);b/=85
print'<~%s~>'%o[k:][::-1]

Dies ist mein erster Code Golf, also bin ich sicher, dass etwas mit meinem Ansatz nicht stimmt. Ich wollte auch base85 implementieren, anstatt nur die Bibliotheksfunktion aufzurufen. :) :)

David
quelle
Das sind 181 Bytes. Vergessen Sie nicht, die neue Zeile zu entfernen, die IDLE Ihrem Code beim Speichern hinzufügt (wenn Sie IDLE verwenden). Sie rufen die Funktion auch nie auf oder erhalten keine Benutzereingaben, sodass sie beim Ausführen nichts bewirkt.
Zach Gates
War nicht sicher, ob es eine Funktion sein oder E / A lesen sollte oder was ... sollte es stdin lesen und stdout drucken? (Wieder noch nie Code Golf gemacht ...)
David
Willkommen bei Programming Puzzles & Code Golf! Es scheint ein Problem mit Eingabelängen zu geben, die nicht durch 4 teilbar sind (letzte 2 Testfälle). Zeile 3 sollte lauten [:4+len(s)/4*4]und am Ende der Ausgabe werden keine Zeichen entfernt.
Dennis
Ich glaube, ich habe die Probleme behoben (und leider länger gemacht). Der Versuch, mehr zu optimieren ...
David
Sie können Ihre zweite whileSchleife wie folgt in eine verwandeln : while b:d=chr(b%85+33)+d;b/=85. Sie können auch den Abstand zwischen Ihrer printAnweisung und der Zeichenfolge entfernen . Entfernen Sie außerdem den Abstand zwischen den übergebenen Argumenten s.unpack.
Zach Gates
2

Oktave, 133 131 Bytes

Vielen Dank an @ojdo für den Vorschlag, dass ich Eingaben von argv anstatt von stdin nehme und mir 2 Bytes erspare.

function g(s) p=mod(-numel(s),4);s(end+1:end+p)=0;disp(['<~' dec2base(swapbytes(typecast(s,'uint32')),'!':'u')'(:)'(1:end-p) '~>'])

Ungolfed:

function g(s)             %// function header
p=mod(-numel(s),4);       %// number of missing chars until next multiple of 4
s(end+1:end+p)=0;         %// append p null characters to s
t=typecast(s,'uint32');   %// cast each 4 char block to uint32
u=swapbytes(t);           %// change endian-ness of uint32's
v=dec2base(u,'!':'u');    %// convert to base85
w=v'(:)'(1:end-p);        %// flatten and truncate resulting string
disp(['<~' w '~>']);      %// format and display final result

Ich habe den Code auf ideone gepostet . Die eigenständige Funktion erfordert keine endAnweisung, aber da ideone die Funktion und das aufrufende Skript in derselben Datei hat, ist ein Trennzeichen erforderlich.

Ich habe immer noch nicht herausgefunden, wie ich stdinan Ideone arbeiten kann. Wenn jemand weiß, bin ich immer noch interessiert, also schreibe mir bitte einen Kommentar.

Beispielausgabe von ideone :

easy
<~ARTY*~>
test
<~FCfN8~>
code golf
<~@rGmh+D5V/Ac~>
Programming Puzzles
<~:i^JeEa`g%Bl7Q+:j%)1Ch7Y~>
Becherglas
quelle
Warum nicht einfach benutzen argv()? Die Aufgabenbeschreibung scheint keine Leseeingabe von zu erfordern stdin.
ojdo
Sehr schön! Erlaubt dec2basein Oktave Basen über 36?
Luis Mendo
Wie das Dokument (und die Fehlermeldung) sagen: Das Argument BASEmuss eine Zahl zwischen 2 und 36 oder eine Zeichenfolge sein . Hier 'i':'u'erweitert der Ausdruck die 85-Zeichenfolge !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstu, die als Basis dient.
ojdo
@ojdo Wenn das der Fall ist, sollte ich es zu einer Funktion machen und vielleicht ein paar Bytes speichern.
Becher
1
@ Becher Es tut. Nicht nur die Beschränkung auf 36, sondern auch die Tatsache, dass die Ziffern notwendigerweise 0 ... 9ABC sind, also gibt es einen Sprung in den ASCII-Codes
Luis Mendo
1

Matlab, 175 Bytes

s=input('','s');m=3-mod(numel(s)-1,4);s=reshape([s zeros(1,m)]',4,[])';t=char(mod(floor(bsxfun(@rdivide,s*256.^[3:-1:0]',85.^[4:-1:0])),85)+33)';t=t(:)';['<~' t(1:end-m) '~>']

Beispiel:

>> s=input('','s');m=3-mod(numel(s)-1,4);s=reshape([s zeros(1,m)]',4,[])';t=char(mod(floor(bsxfun(@rdivide,s*256.^[3:-1:0]',85.^[4:-1:0])),85)+33)';t=t(:)';['<~' t(1:end-m) '~>']
code golf
ans =
<~@rGmh+D5V/Ac~>
Luis Mendo
quelle
1

PHP, 181 Bytes

foreach(str_split(bin2hex($argn),8)as$v){for($t="",$d=hexdec(str_pad($v,8,0));$d;$d=$d/85^0)$t=chr($d%85+33).$t;$r.=str_replace("!!!!!",z,substr($t,0,1+strlen($v)/2));}echo"<~$r~>";

Online Version

Erweitert

foreach(str_split(bin2hex($argn),8)as$v){
    for($t="",$d=hexdec(str_pad($v,8,0));$d;$d=$d/85^0)
      $t=chr($d%85+33).$t;
    $r.=str_replace("!!!!!",z,substr($t,0,1+strlen($v)/2));
}
echo"<~$r~>";
Jörg Hülsermann
quelle
1

Pure Bash, ~ 738

Encoder zuerst (etwas Golf):

#!/bin/bash
# Ascii 85 encoder bash script
LANG=C

printf -v n \\%o {32..126};printf -v n "$n";printf -v m %-20sE abtnvfr;p=\<~;l()
{ q=$(($1<<24|$2<<16|$3<<8|$4));q="${n:1+(q/64#378iN)%85:1}${n:1+(q/614125)%85:1
}${n:1+(q/7225)%85:1}${n:1+(q/85)%85:1}${n:1+q%85:1}";};k() { ((${#p}>74))&&ech\
o "${p:0:75}" && p=${p:75};};while IFS= read -rd '' -n 1 q;do [ "$q" ]&&{ print\
f -v q "%q" "$q";case ${#q} in 1|2)q=${n%$q*};o+=($((${#q}+32)));;7)q=${q#*\'\\}
o+=($((8#${q%\'})));;5)q=${q#*\'\\};q=${m%${q%\'}*};o+=($((${#q}+07)));;esac;}||
o+=(0);((${#o[@]}>3))&&{ [ "${o[*]}" = "0 0 0 0" ]&& q=z|| l ${o[@]};p+="${q}";k
o=(); };done;[ "$o" ]&&{ f=0;for((;${#o[@]}<4;)){ o+=(0);((f++));};((f==0))&&[ \
"${o[*]}" = "0 0 0 0" ]&&q=z||l ${o[@]};p+="${q:0:5-f}";};p+="~>";k;[ "$p" ]&&e\
cho "$p"

Tests:

for word in easy test code\ golf Programming\ Puzzles ;do
    printf "%-24s" "$word:"
    ./enc85.sh < <(printf "$word")
  done
easy:                   <~ARTY*~>
test:                   <~FCfN8~>
code golf:              <~@rGmh+D5V/Ac~>
Programming Puzzles:    <~:i^JeEa`g%Bl7Q+:j%)1Ch7Y~>

und Decoder jetzt:

#!/bin/bash
# Ascii 85 decoder bash script
LANG=C

printf -v n "\%o" {33..117};printf -v n "$n";o=1 k=1;j(){ read -r q||o=;[ "$q" \
]&&[ -z "${q//*<~*}" ]&&((k))&&k= q="${q#*<~}";m+="$q";m="${m%~>*}";};l(){ r=;f\
or((i=0;i<${#1};i++)){ s="${1:i:1}";case "$s" in "*"|\\|\?)s=\\${s};;esac;s="${\
n%${s}*}";((r+=${#s}*(85**(4-i))));};printf -v p "\%03o" $((r>>24)) $((r>>16&255
)) $((r>>8&255)) $((r&255));};for((;(o+${#m})>0;)){ [ "$m" ] || j;while [ "${m:0
:1}" = "z" ];do m=${m:1};printf "\0\0\0\0";done;if [ ${#m} -ge 5 ];then q="${m:0
:5}";m=${m:5};l "$q";printf "$p";elif ((o));then j;elif [ "${m##z*}" ];then pri\
ntf -v t %$((5-${#m}))s;l "$m${t// /u}";printf "${p:0:16-4*${#t}}";m=;fi;}

Kopieren Sie diese in enc85.shund dec85.sh, chmod +x {enc,dec}85.shund dann:

./enc85.sh <<<'Hello world!'
<~87cURD]j7BEbo80$3~>
./dec85.sh <<<'<~87cURD]j7BEbo80$3~>'
Hello world!

Aber Sie könnten einen stärkeren Test machen:

ls -ltr --color $HOME/* | gzip | ./enc85.sh | ./dec85.sh | gunzip

Reduziert auf 724 Zeichen:

printf -v n \\%o {32..126};printf -v n "$n";printf -v m %-20sE abtnvfr;p=\<~
l(){ q=$(($1<<24|$2<<16|$3<<8|$4))
q="${n:1+(q/64#378iN)%85:1}${n:1+(q/614125)%85:1}${n:1+(q/7225)%85:1}${n:1+(q/85)%85:1}${n:1+q%85:1}"
};k() { ((${#p}>74))&&echo "${p:0:75}" && p=${p:75};};while IFS= read -rd '' -n 1 q;do [ "$q" ]&&{
printf -v q "%q" "$q";case ${#q} in 1|2)q=${n%$q*};o+=($((${#q}+32)));;7)q=${q#*\'\\}
o+=($((8#${q%\'})));;5)q=${q#*\'\\};q=${m%${q%\'}*};o+=($((${#q}+07)));;esac;}||o+=(0)
((${#o[@]}>3))&&{ [ "${o[*]}" = "0 0 0 0" ]&&q=z||l ${o[@]};p+="${q}";k
o=();};done;[ "$o" ]&&{ f=0;for((;${#o[@]}<4;)){ o+=(0);((f++));}
((f==0))&&[ "${o[*]}" = "0 0 0 0" ]&&q=z||l ${o[@]};p+="${q:0:5-f}";};p+="~>";k;[ "$p" ]&&echo "$p"
F. Hauri
quelle