Codificação Base85

10

O desafio

Escreva um programa que possa receber uma entrada de uma string de linha única contendo qualquer caractere imprimível ASCII e produzir a mesma string codificada no Base85 (usando uma convenção big endian). Você pode assumir que a entrada sempre será ≤ 100 caracteres.


Um guia para a Base85

  • Quatro octetos são codificados em (geralmente) cinco caracteres Base85.

  • Caracteres Base85 variar desde !a u(ASCII 33-117) e z(ASCII 122).

  • Para codificar, você executa continuamente a divisão por 85 nos quatro octetos (um número de 32 bits) e adiciona 33 ao restante (após cada divisão) para obter o caractere ASCII para o valor codificado. Por exemplo, a primeira aplicação desse processo produz o caractere mais à direita no bloco codificado.

  • Se um conjunto de quatro octetos contiver apenas bytes nulos, eles serão codificados como um em zvez de !!!!!.

  • Se o último bloco for menor que quatro octetos, será preenchido com bytes nulos. Após a codificação, o mesmo número de caracteres adicionados como preenchimento é removido do final da saída.

  • O valor codificado deve ser precedido por <~e seguido por ~>.

  • O valor codificado não deve conter espaços em branco (para este desafio).


Exemplos

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

O trecho a seguir codificará uma entrada fornecida para Base85.

Zach Gates
fonte
3
Estou confuso sobre o porquê, dado que você restringe a entrada ao ASCII imprimível, você usa byte como sinônimo de octeto e não permite bytes de 7 bits.
Peter Taylor
Endianness deve ser especificado. Um bloco [0,1,2,3] é convertido para um número de 32 bits como 0x0123 ou 0x3210?
Edc65 23/09/2015
@ edc65 big endian de acordo com o link da wikipedia
Level River St
3
@steveverrill obrigado. Isso deve estar no texto do desafio, e não em um link externo. Pelo menos é em um comentário agora
edc65
Se a entrada pode conter apenas caracteres imprimíveis, como pode conter quatro bytes nulos?
Luis Mendo

Respostas:

9

CJam, 43 39 35 bytes

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

Experimente on-line no intérprete CJam .

Como funciona

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

Se a entrada estiver vazia, N,)será aplicada à sequência "<~". Como Ninicialmente contém um único caractere, a saída estará correta.

Não precisamos lidar com z ou preencher os pedaços codificados com o comprimento 5, pois a entrada conterá apenas caracteres ASCII imprimíveis.

Dennis
fonte
3
Essa solução parece suspeita com a versão Base85 de uma string ASCII (veja o último exemplo em questão). Espere ...
ojdo 23/09/2015
11
@odjo: Existem alguns caracteres inválidos no código CJam, o mais próximo que eu tenho é esta ligação intérprete CJam
schnaader
@ojdo porque o desafio é apenas isso: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())

Eu nunca joguei golfe em Python, então isso provavelmente é sub-ideal.

Obrigado a @ZachGates por jogar fora 3 bytes!

Dennis
fonte
11
Você pode usar em input().encode()vez de str.encode(input())salvar 3 bytes.
Zach Gates
@ZachGates Thanks! Mas tudo isso em decodificação / ainda está me matando.
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]

Este é o meu primeiro código de golfe, então tenho certeza de que há algo errado com minha abordagem. Eu também queria realmente implementar o base85 em vez de apenas chamar a função de biblioteca. :)

David
fonte
Isso é 181 bytes. Não se esqueça de remover a nova linha que o IDLE adiciona ao seu código ao salvar (se você estiver usando o IDLE). Você também nunca chama a função ou obtém a entrada do usuário, para que ela não faça nada quando você a executa.
Zach Gates
Não tinha certeza se deveria ser uma função ou ler E / S ou o que ... deveria ler stdin e imprimir stdout? (Mais uma vez, nunca fez golf código antes ...)
David
Bem-vindo à programação de quebra-cabeças e código de golfe! Parece haver um problema com comprimentos de entrada que não são divisíveis por 4 (últimos 2 casos de teste). A linha 3 deve ser lida [:4+len(s)/4*4]e nenhum caractere é removido do final da saída.
Dennis
Acredito que corrigi os problemas (e infelizmente o tornei mais longo). Tentando otimizar mais ...
David
Você pode transformar seu segundo whileciclo em um como assim: while b:d=chr(b%85+33)+d;b/=85. Você também pode remover o espaço entre sua printdeclaração e a sequência. Além disso, remova o espaço entre os argumentos passados ​​para s.unpack.
Zach Gates
2

Oitava, 133 131 bytes

Agradeço ao @ojdo por sugerir que eu receba a entrada do argv em vez do stdin, economizando 2 bytes.

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

Eu publiquei o código no ideone . A função autônoma não requer uma enddeclaração, mas como o ideone possui a função e o script de chamada no mesmo arquivo, é necessário um separador.

Ainda não consegui descobrir como stdintrabalhar com ideona. Se alguém souber, ainda estou interessado, por favor, escreva-me um comentário.

Saída de amostra do ideone :

easy
<~ARTY*~>
test
<~FCfN8~>
code golf
<~@rGmh+D5V/Ac~>
Programming Puzzles
<~:i^JeEa`g%Bl7Q+:j%)1Ch7Y~>
taça
fonte
Por que não usar apenas argv()? A descrição da tarefa não parece exigir a entrada de leitura stdin.
ojdo 24/09/2015
Muito agradável! O que acontece dec2baseno Octave permite bases acima de 36?
Luis Mendo
Como o documento (e a mensagem de erro) dizem: o argumento BASEdeve ser um número entre 2 e 36 ou uma sequência de símbolos . Aqui, a expressão 'i':'u'expande a cadeia de 85 caracteres !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuque serve como base.
ojdo 24/09/2015
@ojdo Se for esse o caso, devo torná-lo uma função e talvez salvar alguns bytes.
beaker
11
@beaker Sim. Não só a limitação a 36, mas o fato de que os dígitos são necessariamente 0 ... 9ABC, por isso há um salto em códigos ASCII
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) '~>']

Exemplo:

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

Versão Online

Expandido

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
fonte
1

Festa pura, ~ 738

Primeiro codificador (algo jogado):

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

Testes:

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

e decodificador agora:

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

Copiar este em enc85.she dec85.sh, chmod +x {enc,dec}85.shentão:

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

Mas você poderia fazer algum teste mais forte:

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

Reduzido para 724 caracteres:

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
fonte