Encodage Base85

10

Le défi

Écrivez un programme qui peut prendre une entrée d'une chaîne d'une seule ligne contenant tous les caractères imprimables ASCII et produire la même chaîne codée en Base85 (en utilisant une convention big-endian). Vous pouvez supposer que l'entrée sera toujours ≤ 100 caractères.


Un guide de Base85

  • Quatre octets sont codés en (généralement) cinq caractères Base85.

  • Les caractères Base85 vont de !à u(ASCII 33 - 117) et z(ASCII 122).

  • Pour encoder, vous effectuez en continu la division par 85 sur les quatre octets (un nombre de 32 bits) et ajoutez 33 au reste (après chaque division) pour obtenir le caractère ASCII pour la valeur encodée. Par exemple, la première application de ce processus produit le caractère le plus à droite dans le bloc codé.

  • Si un ensemble de quatre octets ne contient que des octets nuls, ils sont codés en tant que zau lieu de !!!!!.

  • Si le dernier bloc est plus court que quatre octets, il est complété par des octets nuls. Après l'encodage, le même nombre de caractères ajoutés en tant que remplissage est supprimé à la fin de la sortie.

  • La valeur codée doit être précédée <~et suivie de ~>.

  • La valeur codée ne doit contenir aucun espace (pour ce défi).


Exemples

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

L'extrait suivant codera une entrée donnée pour Base85.

Portes Zach
la source
3
Je ne comprends pas pourquoi, étant donné que vous limitez l'entrée en ASCII imprimable, vous utilisez ensuite l' octet comme synonyme d' octet et n'autorisez pas les octets de 7 bits.
Peter Taylor
L'endianité doit être spécifiée. Un bloc [0,1,2,3] est converti en un nombre 32 bits comme 0x0123 ou 0x3210?
edc65
@ edc65 big endian selon le lien wikipedia
Level River St
3
@steveverrill merci. Cela devrait figurer dans le texte du défi et non dans un lien externe. Au moins, c'est dans un commentaire maintenant
edc65
Si l'entrée ne peut contenir que des caractères imprimables, comment pourrait-elle contenir quatre octets nuls?
Luis Mendo

Réponses:

9

CJam, 43 39 35 octets

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

Essayez-le en ligne dans l' interpréteur CJam .

Comment ça fonctionne

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

Si l'entrée était vide, N,)s'appliquera à la chaîne "<~". Puisqu'il Ncontient initialement un seul caractère, la sortie sera correcte.

Nous n'avons pas besoin de traiter z ou de remplir les morceaux encodés à la longueur 5, car l'entrée ne contiendra que des caractères ASCII imprimables.

Dennis
la source
3
Cette solution ressemble étrangement à la version Base85 d'une chaîne ASCII (cf. dernier exemple en question). Attendez ...
ojdo
1
@odjo: Il y a des caractères invalides dans le code CJam, le plus proche que j'ai obtenu est ce lien d'interpréteur
CJam
@ojdo parce que le défi est le suivant:a program that can take an input of a single-line string containing any ASCII printable characters,...
edc65
5

Python 3, 71 octets

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

Je n'ai jamais joué au golf en Python, donc c'est probablement sous-optimal.

Merci à @ZachGates d'avoir joué au golf sur 3 octets!

Dennis
la source
1
Vous pouvez utiliser input().encode()au lieu de str.encode(input())pour enregistrer 3 octets.
Zach Gates
@ZachGates Merci! Cependant, tout ce codage / décodage me tue encore.
Dennis
2

Python 2, 193 162 octets

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]

Ceci est mon premier golf de code, donc je suis sûr qu'il y a un problème avec mon approche. Je voulais également implémenter réellement base85 plutôt que d'appeler simplement la fonction de bibliothèque. :)

David
la source
C'est 181 octets. N'oubliez pas de supprimer la nouvelle ligne que IDLE ajoute à votre code lorsque vous enregistrez (si vous utilisez IDLE). Vous n'appelez jamais non plus la fonction, ni n'obtenez l'entrée de l'utilisateur, donc elle ne fait rien lorsque vous l'exécutez.
Zach Gates
Vous ne saviez pas si cela devait être une fonction ou lire les E / S ou quoi ... devrait-il lire stdin et imprimer stdout? (Encore une fois, je n'ai jamais fait de golf de code avant ...)
David
Bienvenue sur Programmation Puzzles & Code Golf! Il semble y avoir un problème avec les longueurs d'entrée qui ne sont pas divisibles par 4 (2 derniers cas de test). La ligne 3 doit être lue [:4+len(s)/4*4]et aucun caractère n'est supprimé à la fin de la sortie.
Dennis
Je pense avoir résolu les problèmes (et malheureusement allongé la durée). Essayer d'optimiser plus ...
David
Vous pouvez transformer votre deuxième whileboucle en un comme comme celui - ci: while b:d=chr(b%85+33)+d;b/=85. Vous pouvez également supprimer l'espace entre votre printinstruction et la chaîne. En outre, supprimez l'espace entre les arguments passés à s.unpack.
Zach Gates
2

Octave, 133 131 octets

Merci à @ojdo d'avoir suggéré de prendre des entrées depuis argv plutôt que stdin, ce qui m'a fait économiser 2 octets.

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

Non golfé:

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

J'ai posté le code sur ideone . La fonction autonome ne nécessite ni endinstruction, mais parce que ideone a la fonction et le script appelant dans le même fichier, elle nécessite un séparateur.

Je n'ai toujours pas réussi à comprendre comment stdintravailler sur l'idéone. Si quelqu'un le sait, je suis toujours intéressé, alors n'hésitez pas à me laisser un commentaire.

Exemple de sortie de ideone :

easy
<~ARTY*~>
test
<~FCfN8~>
code golf
<~@rGmh+D5V/Ac~>
Programming Puzzles
<~:i^JeEa`g%Bl7Q+:j%)1Ch7Y~>
gobelet
la source
Pourquoi ne pas simplement utiliser argv()? La description de la tâche ne semble pas nécessiter de lecture d'entrée stdin.
ojdo
Très agréable! Est-ce dec2basequ'Octave permet des bases supérieures à 36?
Luis Mendo
Comme le doc (et le message d'erreur) le disent: l'argument BASEdoit être un nombre entre 2 et 36, ou une chaîne de symboles . Ici, l'expression 'i':'u'développe la chaîne de 85 caractères !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuqui sert de base.
ojdo
@ojdo Si c'est le cas, je devrais en faire une fonction et peut-être enregistrer quelques octets.
bécher
1
@beaker C'est le cas. Non seulement la limitation à 36, mais le fait que les chiffres sont nécessairement 0 ... 9ABC, donc il y a un saut dans les codes ASCII
Luis Mendo
1

Matlab, 175 octets

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

Exemple:

>> 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
la source
1

PHP, 181 octets

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

Version en ligne

Étendu

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
la source
1

Pure bash, ~ 738

Encodeur en premier (quelque chose de 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~>

et décodeur maintenant:

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

Copiez ceci dans enc85.shet dec85.sh, chmod +x {enc,dec}85.shpuis:

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

Mais vous pourriez faire un test plus fort:

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

Réduit à 724 caractères:

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
la source