Convertir una matriz de bytes a base64

10

Su misión es escribir una función / programa que convierta una matriz de bytes (es decir, una matriz de enteros de 0 a 255), en base64.

El uso de codificadores base64 integrados no está permitido.

La implementación de base64 requerida es RFC 2045. (usando "+", "/", y relleno obligatorio con "=")

¡El código más corto (en bytes) gana!

Ejemplo:

Entrada (int array): [99, 97, 102, 195, 169]

Salida (cadena): Y2Fmw6k=

xem
fuente
¿Qué tipo de competencia es esta?
Cilan
¿ Los codificadores base64 incorporados cubren solo codificadores binarios a texto o funciones que manipulan enteros también?
Dennis
1
Para aclarar: ¿Puedo usar una función que devuelve 1 2el argumento 66?
Dennis
1
Hay 9 versiones estandarizadas o 4 no estandarizadas de base64. Su referencia =para el relleno se reduce a 4. ¿Cuál desea? ¿O quieres una variante no estándar que no tenga longitudes máximas de línea?
Peter Taylor
Supongo que se refirió al "estándar" especificado por RFC 4648 o la versión utilizada por los tipos MIME, RFC 2045. Estos son diferentes, por lo que la aclaración sería muy útil.
semi-extrínseco

Respuestas:

4

JavaScript, 177 187 198 caracteres

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}

Para agregar saltos de línea, \r\ndespués de cada 76 caracteres, agregue 23 caracteres al código:

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}

Código de demostración:

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

Tomás Langkaas
fuente
Buena solución! Puede recortar algunos bytes utilizando algunas funciones de ES6 y eliminando algunas duplicaciones: Código acortado con comentarios
Craig Ayre
@CraigAyre, gracias por su aporte constructivo. ES6 no estaba finalizado y disponible en el momento en que este desafío se publicó originalmente. Como se sugiere en codegolf.meta , puede publicar la versión ES6 abreviada y marcarla como no competitiva.
Tomas Langkaas
No se preocupe, ¡es mi culpa por no verificar la fecha de publicación original! Soy fanático de su solución, así que no voy a publicar otra, pero gracias por el enlace. La lógica literal de la plantilla que eliminó la duplicación del alfabeto se puede convertir a ES5 en el mismo número de bytes, ¡no guarda muchos, pero cada pequeño cuenta!
Craig Ayre
@CraigAyre, gracias de nuevo por la sugerencia, encontró otra forma de comprimir los símbolos de base64 aún más (lo que lo hizo aún más compatible con versiones anteriores, ahora también debería funcionar en IE antiguo).
Tomas Langkaas
3

Ensamblado x86 de 32 bits, 59 bytes

Código de bytes:

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

Desmontaje

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

Llame a b64encode con esi apuntando al búfer de entrada, edi apuntando al búfer de salida.

Podría hacerse aún más pequeño si no se utiliza el ajuste de línea.

Peter Ferrie
fuente
1

perl, 126 bytes

lee stdin, salidas a stdout

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

sin golf:

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
fuente
La pregunta se ha aclarado para requerir RFC 2045, por lo que debe agregar un poco de código para dividir la salida en fragmentos de 76 caracteres y unirse \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}

La función toma una lista de enteros como entrada y emite la cadena, codificada en base64.

Ejemplo:

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

huellas dactilares

Y2Fmw6kA

Sin golf:

Versión que también visualiza los pasos intermedios:

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

Salida:

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=

Pruebas:

Las cadenas de prueba provienen del ejemplo en la pregunta de los ejemplos en el artículo de Wikipedia para 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.";

Prueba de salida:

TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZSO=

YW55IGNhcm5hbCBwbGVhc3VyZSO=

YW55IGNhcm5hbCBwbGVhc3VyZB==

YW55IGNhcm5hbCBwbGVhc3Vy

YW55IGNhcm5hbCBwbGVhc3F=

YW55IGNhcm5hbCBwbGVhcD==

cGxlYXN1cmUu

bGVhc3VyZSO=

ZWFzdXJlLC==

YXN1cmUu

c3VyZSO=
Heiko Oberdiek
fuente
La pregunta se ha aclarado para requerir RFC 2045, por lo que debe agregar un poco de código para dividir la salida en fragmentos de 76 caracteres y unirse \r\n.
Peter Taylor
@PeterTaylor: Gracias, he actualizado la respuesta para RFC 2045.
Heiko Oberdiek
bravo por esta respuesta muy completa. La inclusión de saltos de línea obligatorios (al especificar "RFC 2045" en el OP) fue en realidad un error, de hecho, puede ignorar esa parte. Lo siento :)
xem
1

Python, 234 caracteres

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
fuente
La pregunta se ha aclarado para requerir RFC 2045, por lo que debe agregar un poco de código para dividir la salida en fragmentos de 76 caracteres y unirse \r\n.
Peter Taylor
@PeterTaylor: arreglado.
Keith Randall
1

GolfScript, 80 (77) bytes

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

Lo anterior se ajustará exactamente a 76 caracteres en una línea, excepto la última línea. Todas las líneas están terminadas por CRLF.

Tenga en cuenta que RFC 2045 especifica una longitud de línea máxima variable de 76 caracteres, por lo que a costa de una salida bonita, podemos guardar 3 bytes adicionales.

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

Lo anterior imprimirá un carácter por línea, excepto la última línea, que puede contener 0, 1 o 2 =caracteres. GolfScript también agregará un LF final que, de acuerdo con RFC 2045, debe ser ignorado por el software de decodificación.

Ejemplo

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

Cómo funciona

~          # 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
fuente
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;

Pruébalo en línea!

Podrías reemplazar la cadena ("$s+/=")con una matrizarray_merge(range(A,Z),range(a,z),range(0,9),["+","/","="])

Solo para comparar qué número de bytes puede alcanzar con una función incorporada no permitida

PHP , 45 bytes

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

Pruébalo en línea!

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

Si su navegador no es compatible con ES6, puede probar con esta versión (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])vuelve "Y2Fmw6k=".

Michael M.
fuente
¿Dónde está el código para dividirlo en trozos de 76 caracteres unidos \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

Algo sin golf:

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

Ejemplo :

El módulo base64 incorporado de Python solo se usa en este ejemplo para garantizar que la efunción tenga la salida correcta, la efunción en sí misma no la está usando.

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
fuente
La pregunta se ha aclarado para requerir RFC 2045, por lo que debe agregar un poco de código para dividir la salida en fragmentos de 76 caracteres y unirse \r\n.
Peter Taylor
@PeterTaylor arreglado.
Tony Ellis
0

Jalea , 38 bytes

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

Pruébalo en línea!

Como (casi) cualquier otra respuesta cubre el requisito RFC2045 de "como máximo 76 caracteres por línea con final de línea \r\n", lo seguí.

Cómo funciona

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
fuente
La descompresión básica se puede usar aquí, pero ṃØbṙ1¤es demasiado larga para una operación simple.
user202729
Puede valer la pena pedirle a Dennis que haga un átomo de descompresión de base girada.
user202729
Falla por 0,0,0.
user202729