Kodowanie Base85

10

Wyzwanie

Napisz program, który może pobierać dane wejściowe z ciągu jednowierszowego zawierającego dowolne znaki drukowalne ASCII i wypisywać ten sam ciąg zakodowany w Base85 (używając konwencji big-endian). Możesz założyć, że dane wejściowe zawsze będą miały ≤ 100 znaków.


Przewodnik po Base85

  • Cztery oktety są zakodowane (zwykle) w pięciu znakach Base85.

  • Base85 znaków ma zakres od !do u(ASCII 33–117 ) i z(ASCII 122).

  • Aby zakodować, ciągle wykonujesz dzielenie przez 85 na czterech oktetach (liczba 32-bitowa) i dodajesz 33 do reszty (po każdym dzieleniu), aby uzyskać znak ASCII dla zakodowanej wartości. Na przykład pierwsze zastosowanie tego procesu powoduje, że w zakodowanym bloku jest znak skrajnie prawy.

  • Jeśli zestaw czterech oktetów zawiera tylko bajty puste, są one kodowane jako zzamiast !!!!!.

  • Jeśli ostatni blok jest krótszy niż cztery oktety, jest wypełniany bajtami zerowymi. Po zakodowaniu ta sama liczba znaków, które zostały dodane jako dopełnianie, są usuwane z końca danych wyjściowych.

  • Zakodowana wartość powinna być poprzedzona znakiem <~i po nim ~>.

  • Zakodowana wartość nie powinna zawierać białych znaków (dla tego wyzwania).


Przykłady

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~>

Poniższy fragment kodu koduje dane wejściowe do Base85.

Zach Gates
źródło
3
Jestem zdezorientowany, dlaczego, biorąc pod uwagę, że ograniczyłeś wprowadzanie do drukowalnego ASCII, wtedy używasz bajtu jako synonimu oktetu i nie pozwalasz na 7-bitowe bajty.
Peter Taylor,
Endianness powinien zostać określony. Blok [0,1,2,3] jest konwertowany na liczbę 32-bitową jako 0x0123 lub 0x3210?
edc65,
@ edc65 big endian zgodnie z linkiem wikipedii
Level River St
3
@steveverrill dziękuję. To powinno być w tekście wyzwania, a nie w zewnętrznym łączu. Przynajmniej jest teraz w komentarzu
edc65,
Jeśli dane wejściowe mogą zawierać tylko znaki do wydrukowania, to jak mogą zawierać cztery bajty puste?
Luis Mendo,

Odpowiedzi:

9

CJam, 43 39 35 bajtów

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

Wypróbuj online w interpretatorze CJam .

Jak to działa

"<~"      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.

Jeśli dane wejściowe były puste, N,)zostaną zastosowane do ciągu "<~". Ponieważ Npoczątkowo zawiera pojedynczy znak, wynik będzie poprawny.

Nie musimy zajmować się z ani wstawiać zakodowanych fragmentów do długości 5, ponieważ dane wejściowe będą zawierały tylko drukowalne znaki ASCII.

Dennis
źródło
3
To rozwiązanie wygląda podejrzanie jak wersja Base85 ciągu ASCII (por. Ostatni przykład, o którym mowa). Poczekaj ...
ojdo
1
@odjo: W kodzie CJam jest kilka niepoprawnych znaków, najbliższy jest link do interpretera
CJam
@ojdo, ponieważ wyzwanie jest takie:a program that can take an input of a single-line string containing any ASCII printable characters,...
edc65
5

Python 3, 71 bajtów

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

Nigdy nie grałem w golfa w Pythonie, więc prawdopodobnie nie jest to optymalne.

Dzięki @ZachGates za grę w golfa z 3 bajtów!

Dennis
źródło
1
Możesz użyć input().encode()zamiast str.encode(input())zapisać 3 bajty.
Zach Gates
@ZachGates Thanks! Całe to en- / dekodowanie wciąż mnie zabija.
Dennis,
2

Python 2, 193 162 bajty

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]

To jest mój pierwszy kod golfowy, więc jestem pewien, że coś jest nie tak z moim podejściem. Chciałem także zaimplementować base85 zamiast wywoływać funkcję biblioteki. :)

David
źródło
To jest 181 bajtów. Nie zapomnij usunąć nowego wiersza, który IDLE dodaje do kodu podczas zapisywania (jeśli używasz IDLE). Nigdy też nie wywołujesz funkcji ani nie dostajesz informacji od użytkownika, więc po uruchomieniu nie robi ona nic.
Zach Gates,
Nie byłem pewien, czy powinna to być funkcja, czy odczyt I / O, czy co ... powinien odczytać standardowe wejście i wydrukować standardowe wyjście? (Znów, nigdy wcześniej nie grałem w golfa kodowego ...)
David
Witamy w Programowaniu Puzzle i Code Golf! Wydaje się, że występuje problem z długościami wejściowymi, których nie można podzielić przez 4 (ostatnie 2 przypadki testowe). Wiersz 3 powinien zostać odczytany [:4+len(s)/4*4]i żadne znaki nie są usuwane z końca wyniku.
Dennis,
Wydaje mi się, że naprawiłem problemy (i niestety wydłużyłem je). Próbuję zoptymalizować więcej ...
David
Można włączyć swoją drugą whilepętlę do jednej jak tak: while b:d=chr(b%85+33)+d;b/=85. Możesz także usunąć spację między printwyciągiem a ciągiem. Ponadto usuń spację między przekazanymi argumentami s.unpack.
Zach Gates,
2

Oktawa, 133 131 bajtów

Dzięki @ojdo za sugestię, że biorę dane z argv zamiast standardowego, co oszczędza mi 2 bajty.

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

Nie golfowany:

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

Opublikowałem kod na ideone . Samodzielna funkcja nie wymaga endinstrukcji i instrukcji, ale ponieważ ideone ma funkcję i skrypt wywołujący w tym samym pliku, wymaga separatora.

Nadal nie byłem w stanie dowiedzieć się, jak zacząć stdinpracować nad ideonem. Jeśli ktoś wie, nadal jestem zainteresowany, więc proszę o komentarz.

Przykładowe dane wyjściowe z ideone :

easy
<~ARTY*~>
test
<~FCfN8~>
code golf
<~@rGmh+D5V/Ac~>
Programming Puzzles
<~:i^JeEa`g%Bl7Q+:j%)1Ch7Y~>
zlewka
źródło
Dlaczego nie po prostu użyć argv()? Opis zadania nie wymaga odczytywania danych wejściowych stdin.
ojdo
Bardzo dobrze! Czy dec2basew Octave dopuszcza zasady powyżej 36?
Luis Mendo
Jak mówi dokument (i komunikat o błędzie): argument BASEmusi być liczbą od 2 do 36 lub ciągiem symboli . Tutaj wyrażenie 'i':'u'rozszerza ciąg znaków o długości 85 znaków, !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuktóry służy jako podstawa.
ojdo
@ojdo Jeśli tak jest, powinienem zrobić z tego funkcję i może zaoszczędzić kilka bajtów.
zlewka
1
@beaker To robi. Nie tylko ograniczenie do 36, ale fakt, że cyfry są koniecznie 0 ... 9ABC, więc jest skok w kodach ASCII
Luis Mendo
1

Matlab, 175 bajtów

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) '~>']

Przykład:

>> 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
źródło
1

PHP, 181 bajtów

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~>";

Wersja online

Rozszerzony

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
źródło
1

Pure Bash, ~ 738

Najpierw koder (coś golfowego):

#!/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"

Testy:

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~>

i dekoder teraz:

#!/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;}

Skopiuj to na enc85.shi dec85.sh, chmod +x {enc,dec}85.sh, a następnie:

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

Ale możesz zrobić silniejszy test:

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

Zmniejszony do 724 znaków:

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
źródło