Pengkodean Base85

10

Tantangan

Tulis sebuah program yang dapat mengambil input dari string baris tunggal yang mengandung karakter ASCII yang dapat dicetak, dan output string yang sama yang disandikan dalam Base85 (menggunakan konvensi big-endian). Anda dapat mengasumsikan bahwa input akan selalu menjadi ≤ 100 karakter.


Panduan untuk Base85

  • Empat oktet dikodekan menjadi (biasanya) lima karakter Base85.

  • Karakter Base85 berkisar dari !hingga u(ASCII 33 - 117) dan z(ASCII 122).

  • Untuk menyandikan, Anda terus melakukan pembagian dengan 85 pada empat oktet (angka 32-bit), dan menambahkan 33 ke sisanya (setelah setiap divisi) untuk mendapatkan karakter ASCII untuk nilai yang disandikan. Misalnya, aplikasi pertama dari proses ini menghasilkan karakter paling kanan di blok yang disandikan.

  • Jika satu set empat oktet hanya berisi byte nol, mereka dikodekan sebagai zgantinya !!!!!.

  • Jika blok terakhir lebih pendek dari empat oktet, itu diisi dengan byte nol. Setelah penyandian, jumlah karakter yang sama yang ditambahkan sebagai padding, dihilangkan dari akhir output.

  • Nilai yang disandikan harus didahului oleh <~dan diikuti oleh ~>.

  • Nilai yang dikodekan tidak boleh mengandung spasi putih (untuk tantangan ini).


Contohnya

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

Cuplikan berikut akan menyandikan input yang diberikan ke Base85.

Zach Gates
sumber
3
Saya bingung mengapa, mengingat bahwa Anda membatasi input ke ASCII yang dapat dicetak, Anda kemudian menggunakan byte sebagai sinonim dari oktet dan tidak mengizinkan byte 7-bit.
Peter Taylor
Endianness harus ditentukan. Blok [0,1,2,3] dikonversi ke angka 32 bit sebagai 0x0123 atau 0x3210?
edc65
@ edc65 big endian sesuai dengan tautan wikipedia
Level River St
3
@steveverrill terima kasih. Itu harus ada di teks tantangan, dan bukan di tautan eksternal. Setidaknya itu dalam komentar sekarang
edc65
Jika input hanya dapat berisi karakter yang dapat dicetak, bagaimana bisa mengandung empat byte nol?
Luis Mendo

Jawaban:

9

CJam, 43 39 35 byte

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

Cobalah online di penerjemah CJam .

Bagaimana itu bekerja

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

Jika input kosong, N,)akan diterapkan ke string "<~". Karena Nawalnya memegang satu karakter, hasilnya akan benar.

Kami tidak harus berurusan dengan z atau pad potongan yang disandikan hingga panjang 5, karena input hanya akan berisi karakter ASCII yang dapat dicetak.

Dennis
sumber
3
Solusi ini tampak mencurigakan seperti versi Base85 dari string ASCII (lih. Contoh terakhir dalam pertanyaan). Tunggu ...
ojdo
1
@odjo: Ada beberapa karakter yang tidak valid dalam kode CJam, yang paling dekat yang saya dapatkan adalah tautan juru bahasa CJam ini
schnaader
@ojdo karena tantangannya hanya ini:a program that can take an input of a single-line string containing any ASCII printable characters,...
edc65
5

Python 3, 71 byte

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

Saya tidak pernah bermain golf di Python, jadi ini mungkin kurang optimal.

Berkat @ZachGates untuk bermain golf 3 byte!

Dennis
sumber
1
Anda dapat menggunakan input().encode()alih-alih str.encode(input())menyimpan 3 byte.
Zach Gates
@ZachGates Terima kasih! Semua en- / decoding masih membunuh saya.
Dennis
2

Python 2, 193 162 byte

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]

Ini adalah kode golf pertama saya, jadi saya yakin ada yang salah dengan pendekatan saya. Saya juga ingin benar-benar mengimplementasikan base85 daripada hanya memanggil fungsi perpustakaan. :)

David
sumber
Ini adalah 181 byte. Jangan lupa untuk menghapus baris baru yang ditambahkan IDLE ke kode Anda ketika Anda menyimpan (jika Anda menggunakan IDLE). Anda juga tidak pernah memanggil fungsi, atau mendapatkan input pengguna, sehingga tidak melakukan apa-apa saat Anda menjalankannya.
Zach Gates
Tidak yakin apakah itu harus berfungsi atau membaca I / O atau apa ... haruskah itu membaca stdin dan mencetak stdout? (Sekali lagi, tidak pernah melakukan golf kode sebelumnya ...)
David
Selamat Datang di Programming Puzzles & Code Golf! Tampaknya ada masalah dengan panjang input yang tidak habis dibagi oleh 4 (2 kasus uji terakhir). Baris 3 harus dibaca [:4+len(s)/4*4]dan tidak ada karakter yang dihapus dari akhir output.
Dennis
Saya percaya saya telah memperbaiki masalah (dan sayangnya membuatnya lebih lama). Mencoba mengoptimalkan lebih banyak ...
David
Anda dapat mengubah kedua whilelingkaran menjadi satu seperti seperti ini: while b:d=chr(b%85+33)+d;b/=85. Anda juga dapat menghapus ruang antara printpernyataan Anda dan string. Selain itu, hapus ruang di antara argumen yang diteruskan ke s.unpack.
Zach Gates
2

Oktaf, 133 131 byte

Terima kasih kepada @ojdo karena menyarankan saya mengambil input dari argv daripada stdin, menyelamatkan saya 2 byte.

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

Tidak Disatukan:

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

Saya telah memposting kode di ideone . Fungsi mandiri tidak memerlukan dan endpernyataan, tetapi karena ideone memiliki fungsi dan skrip panggilan dalam file yang sama itu membutuhkan pemisah.

Saya masih belum bisa memikirkan bagaimana cara stdinmengerjakan ideone. Jika ada yang tahu, saya masih tertarik, jadi tolong beri saya komentar.

Contoh keluaran dari ideone :

easy
<~ARTY*~>
test
<~FCfN8~>
code golf
<~@rGmh+D5V/Ac~>
Programming Puzzles
<~:i^JeEa`g%Bl7Q+:j%)1Ch7Y~>
gelas kimia
sumber
Kenapa tidak pakai saja argv()? Deskripsi tugas tampaknya tidak memerlukan input bacaan dari stdin.
ojdo
Sangat bagus! Jadi apakah dec2basedalam Oktaf memungkinkan basis di atas 36?
Luis Mendo
Seperti yang dikatakan oleh doc (dan pesan kesalahan): argumen BASEharus berupa angka antara 2 dan 36, atau serangkaian simbol . Di sini, ekspresi 'i':'u'memperluas string 85 karakter !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuyang berfungsi sebagai basis.
ojdo
@ojdo Jika itu masalahnya maka saya harus membuatnya berfungsi dan mungkin menyimpan beberapa byte.
Gelas kimia
1
@ speaker Tidak. Tidak hanya batasan untuk 36, tetapi fakta bahwa angka harus 0 ... 9ABC, jadi ada lompatan dalam kode ASCII
Luis Mendo
1

Matlab, 175 byte

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

Contoh:

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

Versi Online

Diperluas

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

Bash murni, ~ 738

Encoder terlebih dahulu (sesuatu yang 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"

Tes:

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

dan dekoder sekarang:

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

Salin ini dalam enc85.shdan dec85.sh, chmod +x {enc,dec}85.sh, maka:

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

Tetapi Anda bisa melakukan beberapa tes yang lebih kuat:

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

Dikurangi menjadi 724 karakter:

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
sumber