Konvertieren Sie ein Byte-Array in base64

10

Ihre Mission ist es, eine Funktion / ein Programm zu schreiben, die ein Array von Bytes (dh ein Array von Ganzzahlen von 0 bis 255) in base64 konvertiert.

Die Verwendung von integrierten Base64-Encodern ist nicht zulässig.

Die erforderliche base64-Implementierung ist RFC 2045. (unter Verwendung von "+", "/" und obligatorischem Auffüllen mit "=")

Der kürzeste Code (in Bytes) gewinnt!

Beispiel:

Eingabe (int Array): [99, 97, 102, 195, 169]

Ausgabe (Zeichenfolge): Y2Fmw6k=

xem
quelle
Was für ein Wettbewerb ist das?
Cilan
Umfasst eingebaute Base64-Encoder nur Binär-Text-Encoder oder Funktionen, die auch Ganzzahlen bearbeiten?
Dennis
1
Zur Verdeutlichung: Kann ich eine Funktion verwenden, die 1 2für das Argument zurückgibt 66?
Dennis
1
Es gibt 9 standardisierte oder 4 nicht standardisierte Versionen von base64. Ihr Verweis auf =zum Auffüllen schränkt es auf 4 ein. Welches möchten Sie? Oder möchten Sie eine nicht standardmäßige Variante ohne maximale Leitungslänge?
Peter Taylor
Ich vermute, er / sie bezog sich entweder auf den "Standard", der in RFC 4648 spezifiziert ist, oder auf die Version, die von MIME-Typen verwendet wird, RFC 2045. Diese sind unterschiedlich, daher wäre eine Klarstellung sehr nützlich.
semi-extrinsisch

Antworten:

3

32-Bit-x86-Assembly, 59 Byte

Bytecode:

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

Demontage:

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

Rufen Sie b64encode auf, wobei esi auf den Eingabepuffer und edi auf den Ausgabepuffer zeigt.

Es könnte noch kleiner gemacht werden, wenn kein Zeilenumbruch verwendet wird.

Peter Ferrie
quelle
3

JavaScript, 177 187 198 Figuren

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}

Fügen Sie zum Hinzufügen von Zeilenumbrüchen \r\nnach jedem 76. Zeichen 23 Zeichen zum Code hinzu:

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}

Demo-Code:

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
quelle
Schöne Lösung! Sie können einige Bytes mit einigen ES6-Funktionen rasieren und einige Duplikate entfernen: Verkürzter Code mit Kommentaren
Craig Ayre
@CraigAyre, danke für die konstruktive Eingabe. ES6 war zum Zeitpunkt der Veröffentlichung dieser Herausforderung noch nicht fertiggestellt und verfügbar. Wie unter codegolf.meta vorgeschlagen , können Sie die verkürzte ES6-Version veröffentlichen und als nicht konkurrierend markieren.
Tomas Langkaas
Keine Sorge, ich bin schuld daran, dass ich das ursprüngliche Post-Datum nicht noch einmal überprüft habe! Ich bin ein Fan Ihrer Lösung, daher werde ich keine weitere veröffentlichen, aber danke für den Link. Die Vorlagenliterallogik, mit der die Alphabet-Duplizierung entfernt wurde, kann in der gleichen Anzahl von Bytes in ES5 konvertiert werden. Sie spart nicht viele, aber jede Kleinigkeit zählt!
Craig Ayre
@CraigAyre, nochmals vielen Dank für den Tipp, hat eine andere Möglichkeit gefunden, die base64-Symbole noch stärker zu komprimieren (wodurch es noch abwärtskompatibler wurde - sollte jetzt auch im alten IE funktionieren).
Tomas Langkaas
1

Perl, 126 Bytes

liest stdin, gibt an stdout aus

$/=$\;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

ungolfed:

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
quelle
Die Frage wurde geklärt, um RFC 2045 zu erfordern. Sie müssen also ein bisschen Code hinzufügen, um die Ausgabe in 76-Zeichen-Blöcke aufzuteilen und mit zu verbinden \r\n.
Peter Taylor
1

Perl, 147 Bytes

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}

Die Funktion verwendet eine Liste von Ganzzahlen als Eingabe und gibt die Zeichenfolge base64-codiert aus.

Beispiel:

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

druckt

Y2Fmw6kA

Ungolfed:

Version, die auch die Zwischenschritte visualisiert:

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;
}

Ausgabe:

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=

Tests:

Die Testzeichenfolgen stammen aus dem Beispiel in der Frage der Beispiele im Wikipedia-Artikel für 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.";

Testausgabe:

TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZSO=

YW55IGNhcm5hbCBwbGVhc3VyZSO=

YW55IGNhcm5hbCBwbGVhc3VyZB==

YW55IGNhcm5hbCBwbGVhc3Vy

YW55IGNhcm5hbCBwbGVhc3F=

YW55IGNhcm5hbCBwbGVhcD==

cGxlYXN1cmUu

bGVhc3VyZSO=

ZWFzdXJlLC==

YXN1cmUu

c3VyZSO=
Heiko Oberdiek
quelle
Die Frage wurde geklärt, um RFC 2045 zu erfordern. Sie müssen also ein bisschen Code hinzufügen, um die Ausgabe in 76-Zeichen-Blöcke aufzuteilen und mit zu verbinden \r\n.
Peter Taylor
@ PeterTaylor: Danke, ich habe die Antwort für RFC 2045 aktualisiert.
Heiko Oberdiek
Bravo für diese sehr vollständige Antwort. Das Einfügen von obligatorischen Zeilenumbrüchen (durch Angabe von "RFC 2045" im OP) war tatsächlich ein Fehler. Sie können diesen Teil sogar ignorieren. Entschuldigung :)
xem
1

Python, 234 Zeichen

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
quelle
Die Frage wurde geklärt, um RFC 2045 zu erfordern. Sie müssen also ein bisschen Code hinzufügen, um die Ausgabe in 76-Zeichen-Blöcke aufzuteilen und mit zu verbinden \r\n.
Peter Taylor
@ PeterTaylor: behoben.
Keith Randall
1

GolfScript, 80 (77) Bytes

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

Das Obige passt genau 76 Zeichen in eine Zeile, mit Ausnahme der letzten Zeile. Alle Leitungen werden von CRLF abgeschlossen.

Beachten Sie, dass RFC 2045 eine variable, maximale Zeilenlänge von 76 Zeichen angibt , sodass wir auf Kosten einer hübschen Ausgabe 3 zusätzliche Bytes sparen können.

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

Das obige Zeichen gibt ein Zeichen pro Zeile aus, mit Ausnahme der letzten Zeile, die 0, 1 oder 2 =Zeichen enthalten kann. GolfScript fügt außerdem einen endgültigen LF hinzu, der laut RFC 2045 von der Decodierungssoftware ignoriert werden muss.

Beispiel

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

Wie es funktioniert

~          # 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
quelle
1

PHP , 200 Bytes

<?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;

Probieren Sie es online aus!

Sie können die Zeichenfolge ("$s+/=")durch ein Array ersetzenarray_merge(range(A,Z),range(a,z),range(0,9),["+","/","="])

Nur um zu vergleichen, welche Byteanzahl mit einer nicht erlaubten eingebauten erreicht werden kann

PHP , 45 Bytes

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

Probieren Sie es online aus!

Jörg Hülsermann
quelle
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}

Wenn Ihr Browser ES6 nicht unterstützt, können Sie es mit dieser Version (262B) versuchen:

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])kehrt zurück "Y2Fmw6k=".

Michael M.
quelle
Wo ist der Code, um ihn in Chunks mit 76 Zeichen aufzuteilen \r\n?
Peter Taylor
0

Python - 310333

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

Etwas ungolf:

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

Beispiel :

Das in Python integrierte base64-Modul wird in diesem Beispiel nur verwendet, um sicherzustellen, dass die eFunktion die richtige Ausgabe hat und von der eFunktion selbst nicht verwendet wird.

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
quelle
Die Frage wurde geklärt, um RFC 2045 zu erfordern. Sie müssen also ein bisschen Code hinzufügen, um die Ausgabe in 76-Zeichen-Blöcke aufzuteilen und mit zu verbinden \r\n.
Peter Taylor
@ PeterTaylor behoben.
Tony Ellis
0

Gelee , 38 Bytes

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

Probieren Sie es online aus!

Da (fast) jede andere Antwort die RFC2045-Anforderung "höchstens 76 Zeichen pro Zeile mit Zeilenende \r\n" abdeckt , habe ich sie befolgt.

Wie es funktioniert

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
quelle
Die Basisdekomprimierung kann hier verwendet werden, ist jedoch ṃØbṙ1¤für eine einfache Operation etwas zu lang.
user202729
Es könnte sich lohnen, Dennis zu bitten, ein Atom mit gedrehter Base-Dekompression herzustellen.
user202729
Schlägt für 0,0,0.
user202729