Konwertuj tablicę bajtów na base64

10

Twoim zadaniem jest napisanie funkcji / programu, który konwertuje tablicę bajtów (tj. Tablicę liczb całkowitych od 0 do 255) na base64.

Korzystanie z wbudowanych koderów base64 jest niedozwolone.

Wymagana implementacja base64 to RFC 2045. (użycie „+”, „/” i obowiązkowe wypełnienie za pomocą „=”)

Najkrótszy kod (w bajtach) wygrywa!

Przykład:

Dane wejściowe (tablica int): [99, 97, 102, 195, 169]

Dane wyjściowe (ciąg): Y2Fmw6k=

Xem
źródło
Co to za konkurs?
Cilan
Czy wbudowane kodery base64 obejmują tylko kodery binarne na tekst lub funkcje manipulujące liczbami całkowitymi?
Dennis
1
Wyjaśnienie: Czy mogę użyć funkcji zwracającej 1 2argument 66?
Dennis
1
Istnieje 9 znormalizowanych lub 4 niestandardowe wersje base64. Twoje odniesienie do =padding zawęża go do 4. Którego chcesz? A może chcesz niestandardowy wariant, który nie ma maksymalnych długości linii?
Peter Taylor
Zgaduję, że odnosił się do „standardowego” określonego przez RFC 4648 lub wersji używanej przez typy MIME, RFC 2045. Są one różne, więc wyjaśnienie byłoby bardzo przydatne.
pół-zewnętrzny zewnętrzny

Odpowiedzi:

4

JavaScript, 177 187 198 postacie

function(d){c="";for(a=e=b=0;a<4*d.length/3;f=b>>2*(++a&3)&63,c+=String.fromCharCode(f+71-(f<26?6:f<52?0:f<62?75:f^63?90:87)))a&3^3&&(b=b<<8^d[e++]);for(;a++&3;)c+="=";return c}

W celu dodania podziału linii, \r\npo każdym 76 znaku, dodaj 23 znaki do kodu:

function(d){c="";for(a=e=b=0;a<4*d.length/3;f=b>>2*(++a&3)&63,c+=String.fromCharCode(f+71-(f<26?6:f<52?0:f<62?75:f^63?90:87))+(75==(a-1)%76?"\r\n":""))a&3^3&&(b=b<<8^d[e++]);for(;a++&3;)c+="=";return c}

Kod demonstracyjny:

var encode = function(d,a,e,b,c,f){c="";for(a=e=b=0;a<4*d.length/3;f=b>>2*(++a&3)&63,c+=String.fromCharCode(f+71-(f<26?6:f<52?0:f<62?75:f^63?90:87))+(75==(a-1)%76?"\r\n":""))a&3^3&&(b=b<<8^d[e++]);for(;a++&3;)c+="=";return c};

//OP test case
console.log(encode([99, 97, 102, 195, 169])); // outputs "Y2Fmw6k=".

//Quote from Hobbes' Leviathan:
console.log(
 encode(
  ("Man is distinguished, not only by his reason, but by this singular passion from " +
   "other animals, which is a lust of the mind, that by a perseverance of delight " +
   "in the continued and indefatigable generation of knowledge, exceeds the short " +
   "vehemence of any carnal pleasure.")
  .split('').map(function(i){return i.charCodeAt(0)})
 )
);

Tomas Langkaas
źródło
Fajne rozwiązanie! Możesz ogolić niektóre bajty za pomocą niektórych funkcji ES6 i usunąć trochę powielania: Skrócony kod z komentarzami
Craig Ayre
@CraigAyre, dzięki za konstruktywny wkład. ES6 nie został ukończony i był dostępny w momencie opublikowania tego wyzwania. Jak sugerowano w codegolf.meta , możesz opublikować skróconą wersję ES6 i oznaczyć ją jako niekonkurującą.
Tomas Langkaas,
Nie martw się, moja wina, że ​​nie sprawdziłem dwukrotnie oryginalnej daty postu! Jestem fanem twojego rozwiązania, więc nie zamierzam publikować innego, ale dziękuję za link. Dosłowna logika szablonu, która usunęła duplikację alfabetu, może zostać przekonwertowana na ES5 w tej samej liczbie bajtów, nie zapisuje wielu, ale co najmniej liczy się!
Craig Ayre
@CraigAyre, jeszcze raz dziękuję za podpowiedź, znalazł jeszcze inny sposób na kompresowanie symboli base64 jeszcze bardziej (dzięki czemu był jeszcze bardziej kompatybilny wstecz - powinien teraz działać również w starym IE).
Tomas Langkaas,
3

32-bitowy zestaw x86, 59 bajtów

Kod bajtowy:

66 B8 0D 0A 66 AB 6A 14 5A 4A 74 F4 AD 4E 45 0F C8 6A 04 59 C1 C0 06 24 3F 3C 3E 72 05 C0
E0 02 2C 0E 2C 04 3C 30 7D 08 04 45 3C 5A 76 02 04 06 AA 4D E0 E0 75 D3 B0 3D F3 AA C3

Demontaż:

b64_newline:
    mov     ax, 0a0dh
    stosw
b64encode:
    push    (76 shr 2) + 1
    pop     edx
b64_outer:
    dec     edx
    je      b64_newline
    lodsd
    dec     esi
    inc     ebp
    bswap   eax
    push    4
    pop     ecx
b64_inner:
    rol     eax, 6
    and     al, 3fh
    cmp     al, 3eh
    jb      b64_testchar
    shl     al, 2     ;'+' and '/' differ by only 1 bit
    sub     al, ((3eh shl 2) + 'A' - '+') and 0ffh
b64_testchar:
    sub     al, 4
    cmp     al, '0'
    jnl     b64_store ;l not b because '/' is still < 0 here
    add     al, 'A' + 4
    cmp     al, 'Z'
    jbe     b64_store
    add     al, 'a' - 'Z' - 1
b64_store:
    stosb
    dec     ebp
    loopne  b64_inner
    jne     b64_outer
    mov     al, '='
    rep     stosb
    ret

Wywołaj b64encode z esi wskazującym na bufor wejściowy, edi wskazującym na bufor wyjściowy.

Można go jeszcze zmniejszyć, jeśli nie stosuje się zawijania linii.

Peter Ferrie
źródło
1

perl, 126 bajtów

czyta standardowe wejście, wyświetla standardowe wyjście

$/=$\;print map{$l=y///c/2%3;[A..Z,a..z,0..9,"+","/"]->[oct"0b".substr$_.0 x4,0,6],$l?"="x(3-$l):""}unpack("B*",<>)=~/.{1,6}/g

bez golfa:

my @x = ('A'..'Z','a'..'z',0..9,'+','/');
my $in = join '', <>;
my $bits = unpack 'B*', $in;
my @six_bit_groups = $bits =~ /.{1,6}/g;
for my $sixbits (@six_bit_groups) {
  next unless defined $sixbits;
  $l=length($sixbits)/2%3;
  my $zero_padded = $sixbits . ( "0" x 4 );
  my $padded_bits = substr( $zero_padded, 0, 6 );
  my $six_bit_int = oct "0b" . $padded_bits;
  print $x[$six_bit_int];
  print "=" x (3 - $l)  if  $l;
}
skibrianski
źródło
Pytanie zostało wyjaśnione, aby wymagało RFC 2045, więc musisz dodać trochę kodu, aby podzielić dane wyjściowe na 76-znakowe fragmenty i dołączyć \r\n.
Peter Taylor
1

Perl, 147 bajtów

sub b{$f=(3-($#_+1)%3)%3;$_=unpack'B*',pack'C*',@_;@r=map{(A..Z,a..z,0..9,'+','/')[oct"0b$_"]}/.{1,6}/g;$"='';join"\r\n",("@r".'='x$f)=~/.{1,76}/g}

Funkcja przyjmuje listę liczb całkowitych jako dane wejściowe i wyprowadza ciąg, zakodowany w standardzie base64.

Przykład:

print b(99, 97, 102, 195, 169)

odciski

Y2Fmw6kA

Nie golfowany:

Wersja, która wizualizuje również etapy pośrednie:

sub b {
    # input array: @_
    # number of elements: $#_ + 1 ($#_ is zero-based index of last element in 
    $fillbytes = (3 - ($#_ + 1) % 3) % 3;
      # calculate the number for the needed fill bytes
      print "fillbytes:       $fillbytes\n";
    $byte_string = pack 'C*', @_;
      # the numbers are packed as octets to a binary string
      # (binary string not printed)
    $bit_string = unpack 'B*', $byte_string;
      # the binary string is converted to its bit representation, a string wit
      print "bit string:      \"$bit_string\"\n";
    @six_bit_strings = $bit_string =~ /.{1,6}/g;
      # group in blocks of 6 bit
      print "6-bit strings:   [@six_bit_strings]\n";
    @index_positions = map { oct"0b$_" } @six_bit_strings;
      # convert bit string to number
      print "index positions: [@index_positions]\n";
    @alphabet = (A..Z,a..z,0..9,'+','/');
      # the alphabet for base64
    @output_chars = map { $alphabet[$_] } @index_positions;
      # output characters with wrong last characters that entirely derived fro
      print "output chars:    [@output_chars]\n";
    local $" = ''; #"
    $output_string = "@output_chars";
      # array to string without space between elements ($")
      print "output string:   \"$output_string\"\n";
    $result = $output_string .= '=' x $fillbytes;
      # add padding with trailing '=' characters
      print "result:          \"$result\"\n";
    $formatted_result = join "\r\n", $result =~ /.{1,76}/g;
      # maximum line length is 76 and line ends are "\r\n" according to RFC 2045
      print "formatted result:\n$formatted_result\n";
    return $formatted_result;
}

Wynik:

fillbytes:       1
bit string:      "0110001101100001011001101100001110101001"
6-bit strings:   [011000 110110 000101 100110 110000 111010 1001]
index positions: [24 54 5 38 48 58 9]
output chars:    [Y 2 F m w 6 J]
output string:   "Y2Fmw6J"
result:          "Y2Fmw6J="
formatted result:
Y2Fmw6J=

Testy:

Ciągi testowe pochodzą z przykładu w pytaniu o przykłady w artykule Wikipedii dla Base64 .

sub b{$f=(3-($#_+1)%3)%3;$_=unpack'B*',pack'C*',@_;@r=map{(A..Z,a..z,0..9,'+','/')[oct"0b$_"]}/.{1,6}/g;$"='';join"\r\n",("@r".'='x$f)=~/.{1,76}/g}

sub test ($) {
   print b(map {ord($_)} $_[0] =~ /./sg), "\n\n";
}

my $str = <<'END_STR';
Man is distinguished, not only by his reason, but by this singular passion from
other animals, which is a lust of the mind, that by a perseverance of delight
in the continued and indefatigable generation of knowledge, exceeds the short
vehemence of any carnal pleasure.
END_STR
chomp $str;

test "\143\141\146\303\251";
test $str;
test "any carnal pleasure.";
test "any carnal pleasure";
test "any carnal pleasur";
test "any carnal pleasu";
test "any carnal pleas";
test "pleasure.";
test "leasure.";
test "easure.";
test "asure.";
test "sure.";

Wyjście testowe:

TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZSO=

YW55IGNhcm5hbCBwbGVhc3VyZSO=

YW55IGNhcm5hbCBwbGVhc3VyZB==

YW55IGNhcm5hbCBwbGVhc3Vy

YW55IGNhcm5hbCBwbGVhc3F=

YW55IGNhcm5hbCBwbGVhcD==

cGxlYXN1cmUu

bGVhc3VyZSO=

ZWFzdXJlLC==

YXN1cmUu

c3VyZSO=
Heiko Oberdiek
źródło
Pytanie zostało wyjaśnione, aby wymagało RFC 2045, więc musisz dodać trochę kodu, aby podzielić dane wyjściowe na 76-znakowe fragmenty i dołączyć \r\n.
Peter Taylor
@PeterTaylor: Dzięki, zaktualizowałem odpowiedź dla RFC 2045.
Heiko Oberdiek
brawo za tę bardzo kompletną odpowiedź. Uwzględnienie obowiązkowych podziałów linii (przez określenie „RFC 2045” w PO) było faktycznie błędem, w rzeczywistości można zignorować tę część. Przepraszamy :)
xem
1

Python, 234 znaki

def F(s):
 R=range;A=R(65,91)+R(97,123)+R(48,58)+[43,47];n=len(s);s+=[0,0];r='';i=0
 while i<n:
  if i%57<1:r+='\r\n'
  for j in R(4):r+=chr(A[s[i]*65536+s[i+1]*256+s[i+2]>>18-6*j&63])
  i+=3
 k=-n%3
 if k:r=r[:-k]+'='*k
 return r[2:]
Keith Randall
źródło
Pytanie zostało wyjaśnione, aby wymagało RFC 2045, więc musisz dodać trochę kodu, aby podzielić dane wyjściowe na 76-znakowe fragmenty i dołączyć \r\n.
Peter Taylor
@PeterTaylor: naprawiono.
Keith Randall
1

GolfScript, 80 (77) bajtów

~.,~)3%:P[0]*+[4]3*\+256base 64base{'+/''A[a{:0'{,^}/=}/{;}P*'='P*]4>76/"\r
":n*

Powyższe zmieści dokładnie 76 znaków w linii, z wyjątkiem ostatniej linii. Wszystkie linie są zakończone przez CRLF.

Zauważ, że RFC 2045 określa zmienną, maksymalną długość linii 76 znaków, więc kosztem ładnego wyniku możemy zaoszczędzić 3 dodatkowe bajty.

~.,~)3%:P[0]*+[4]3*\+256base 64base{'+/''A[a{:0'{,^}/=}/{;}P*'='P*]4>{13]n+}/

Powyżej wypisze jeden znak w wierszu, z wyjątkiem ostatniego wiersza, który może zawierać 0, 1 lub 2 =znaki. GolfScript doda również końcowy LF, który zgodnie z RFC 2045 musi zostać zignorowany przez oprogramowanie dekodujące.

Przykład

$ echo '[99 97 102 195 169]' | golfscript base64.gs | cat -A
Y2Fmw6k=^M$
$ echo [ {0..142} ] | golfscript base64.gs | cat -A
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4^M$
OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3Bx^M$
cnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY4=^M$
$ echo '[99 97 102 195 169]' | golfscript base64-sneaky.gs | cat -A
Y^M$
2^M$
F^M$
m^M$
w^M$
6^M$
k^M$
=^M$
$

Jak to działa

~          # Interpret the input string.
.,~)3%:P   # Calculate the number of bytes missing to yield a multiple of 3 and save in “P”.
[0]*+      # Append that many zero bytes to the input array.
[4]3*\+    # Prepend 3 bytes to the input array to avoid issues with leading zeros.
256base    # Convert the input array into an integer.
64base     # Convert that integer to base 64.
{          # For each digit:
  '+/'     # Push '+/'.
  'A[a{:0' # Push 'A[a{:0'.
  {        # For each byte in 'A[a{:0':
    ,      # Push the array of all bytes up to that byte.
    ^      # Take the symmetric difference with the array below it.
  }/       # Result: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  =        # Retrieve the character corresponding to the digit.
}/         #
{;}P*'='P* # Replace the last “P” characters with a string containing that many “=” chars.
]          # Collect all bytes on the stack into an array.
4>         # Remove the first four, which correspond to the 3 prepended bytes.
76/        # Collect all bytes on the stack into an array and split into 76-byte chunks.
"\r\n":n*  # Join the chunks with separator CRLF and save CRLF as the new line terminator.
Dennis
źródło
1

PHP , 200 bajtów

<?foreach($g=$_GET as$k=>$v)$b[$k/3^0]+=256**(2-$k%3)*$v;for(;$i<62;)$s.=chr($i%26+[65,97,48][$i++/26]);foreach($b as$k=>$v)for($i=4;$i--;$p++)$r.=("$s+/=")[count($g)*4/3<$p?64:($v/64**$i)%64];echo$r;

Wypróbuj online!

Możesz zastąpić ciąg ("$s+/=")tablicąarray_merge(range(A,Z),range(a,z),range(0,9),["+","/","="])

Tylko w celu porównania, ile bajtów może osiągnąć przy wbudowanym niedozwolonym

PHP , 45 bajtów

<?=base64_encode(join(array_map(chr,$_GET)));

Wypróbuj online!

Jörg Hülsermann
źródło
0

JavaScript (ES6), 220B

f=a=>{for(s=a.map(e=>('0000000'+e.toString(2)).slice(-8)).join(p='');s.length%6;p+='=')s+='00';return s.match(/.{6}/g).map(e=>'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[parseInt(e,2)]).join('')+p}

Jeśli Twoja przeglądarka nie obsługuje ES6, możesz wypróbować tę wersję (262B):

function f(a){for(s=a.map(function(e){return ('0000000'+e.toString(2)).slice(-8)}).join(p='');s.length%6;p+='=')s+='00';return s.match(/.{6}/g).map(function(e){return 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'[parseInt(e,2)]}).join('')+p}

f([99, 97, 102, 195, 169])zwraca "Y2Fmw6k=".

Michael M.
źródło
Gdzie jest kod dzielący go na 76-znakowe fragmenty połączone \r\n?
Peter Taylor
0

Python - 310, 333

def e(b):
  l=len;c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";r=p="";d=l(b)%3
  if d>0:d=abs(d-3);p+="="*d;b+=[0]*d
  for i in range(0,l(b)-1,3):
    if l(r)%76==0:r+="\r\n"
    n=(b[i]<<16)+(b[i+1]<<8)+b[i+2];x=(n>>18)&63,(n>>12)&63,(n>>6)&63,n&63;r+=c[x[0]]+c[x[1]]+c[x[2]]+c[x[3]]
  return r[:l(r)-l(p)]+p

Nieco golfisty:

def e( b ):
    c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    r = p = ""
    d = len( b ) % 3

    if d > 0:
        d = abs( d - 3 )
        p = "=" * d
        b + = [0] * d

    for i in range( 0, len( b ) - 1, 3 ):
        if len( r ) % 76 == 0:
            r += "\r\n"

        n = ( b[i] << 16 ) + ( b[i + 1] << 8 ) + b[i + 2]
        x = ( n >> 18 ) & 63, ( n >> 12 ) & 63, ( n >> 6) & 63, n & 63
        r += c[x[0]] + c[x[1]] + c[x[2]] + c[x[3]]

    return r[:len( r ) - len( p )] + p

Przykład :

Wbudowany moduł Pythona base64 jest używany tylko w tym przykładzie, aby upewnić się, że efunkcja ma prawidłowe dane wyjściowe, a esama funkcja jej nie używa.

from base64 import encodestring as enc

test = [ 99, 97, 102, 195, 169 ]
str  = "".join( chr( x ) for x in test )

control = enc( str ).strip()
output = e( test )

print output            # => Y2Fmw6k=
print control == output # => True
Tony Ellis
źródło
Pytanie zostało wyjaśnione, aby wymagało RFC 2045, więc musisz dodać trochę kodu, aby podzielić dane wyjściowe na 76-znakowe fragmenty i dołączyć \r\n.
Peter Taylor
@PeterTaylor naprawiony.
Tony Ellis
0

Galaretka , 38 bajtów

s3z0Zµḅ⁹b64‘ịØb)FṖ³LN%3¤¡s4z”=Z;€“ƽ‘Ọ

Wypróbuj online!

Ponieważ (prawie) każda inna odpowiedź obejmuje wymóg RFC2045 dotyczący „najwyżej 76 znaków w linii z zakończeniem linii \r\n”, podążyłem za nią.

Jak to działa

s3z0Zµḅ⁹b64‘ịØb)FṖ³LN%3¤¡s4z”=Z;€“ƽ‘Ọ    Monadic main link. Input: list of bytes

s3z0Z    Slice into 3-item chunks, transpose with 0 padding, transpose back
         Equivalent to "pad to length 3n, then slice into chunks"

µḅ⁹b64‘ịØb)    Convert each chunk to base64
 ḅ⁹b64         Convert base 256 to integer, then to base 64
      ‘ịØb     Increment (Jelly is 1-based) and index into base64 digits

FṖ³LN%3¤¡s4z”=Z    Add correct "=" padding
F                  Flatten the list of strings to single string
 Ṗ      ¡          Repeat "remove last" n times, where
  ³LN%3¤             n = (- input length) % 3
         s4z”=Z    Pad "=" to length 4n, then slice into 4-item chunks

;€“ƽ‘Ọ    Add "\r\n" line separator
;€         Append to each line:
  “ƽ‘       Codepage-encoded list [13,10]
      Ọ    Apply `chr` to numbers; effectively add "\r\n"
Bubbler
źródło
Można tu zastosować bazową dekompresję, ale ṃØbṙ1¤jest ona trochę za długa, aby można było wykonać prostą operację.
user202729,
Być może warto poprosić Dennisa o utworzenie atomu z obróconą zasadą dekompresyjną.
user202729,
Nie działa na 0,0,0.
user202729,