Перетворити масив байтів у base64


10

Ваша місія полягає в тому, щоб написати функцію / програму, яка перетворює масив байтів (тобто: масив цілих чисел від 0 до 255), до base64.

Використання вбудованих кодерів base64 заборонено.

Необхідна реалізація base64 - RFC 2045. (використовуючи "+", "/" та обов'язкове набивання з "=")

Найкоротший код (у байтах) виграє!

Приклад:

Введення (int масив): [99, 97, 102, 195, 169]

Вихід (рядок): Y2Fmw6k=


Що це за конкуренція?
Кілан

Чи вбудовані кодери base64 охоплюють тільки кодери двійкового тексту та функції, що також керують цілими числами?
Денніс

1
Для уточнення: чи можна використовувати функцію, яка повертається 1 2для аргументу 66?
Денніс

1
Існує 9 стандартизованих або 4 нестандартних версії base64. Ваше посилання на =прокладки звужує його до 4. Якого ви бажаєте? Або ви хочете нестандартний варіант, який не має максимальної довжини рядка?
Пітер Тейлор

Я здогадуюсь, що він / вона посилався на "стандартний", визначений RFC 4648, або версію, що використовується типами MIME, RFC 2045. Вони різні, тому уточнення було б дуже корисним.
напівкрединне

Відповіді:


4

JavaScript, 177 187 198 символів

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}

Для додавання рядкових рядків \r\nпісля кожного 76-го символу додайте до коду 23 символи:

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}

Демо-код:

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


Приємне рішення! Ви можете поголити деякі байти, скориставшись деякими функціями ES6 та видаливши кілька дублювань: Скорочений код із коментарями
Craig Ayre

@CraigAyre, дякую за конструктивний внесок. ES6 не був доопрацьований і не був доступний на момент публікації цього виклику. Як запропоновано на коді codegolf.meta , ви можете опублікувати скорочену версію ES6 і позначити її як неконкурентну.
Томаш Лангкаас

Не хвилюйтесь, я винен, що не подвійно перевіряв оригінальну дату публікації! Я шанувальник вашого рішення, тому я не збираюся публікувати інше, але дякую за посилання. Логічна логіка шаблону, що вилучила дублювання алфавіту, може бути перетворена в ES5 в тій же кількості байтів, не економить багато, але кожен маленький рахується!
Крейг Айре

@CraigAyre, ще раз дякую за підказку, знайшов ще один спосіб стиснення символів base64 ще більше (що зробило його ще більш сумісним назад - тепер має працювати і в старому IE).
Томаш Лангкаас

3

32-розрядна збірка x86, 59 байт

Байт-код:

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

Розбирання:

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

Виклик b64encode з esi, що вказує на вхідний буфер, edi вказує на вихідний буфер.

Він може бути ще меншим, якщо не використовується обмотка ліній.


1

perl, 126 байт

читає stdin, виводить у 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

неозорений:

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

Питання було роз'яснено, щоб вимагати RFC 2045, тому вам потрібно додати трохи коду, щоб розділити вихід на 76-шарові фрагменти та приєднатися до \r\n.
Пітер Тейлор

1

Perl, 147 байт

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}

Функція приймає список цілих чисел як вхід і виводить рядок, кодований base64.

Приклад:

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

відбитки

Y2Fmw6kA

Безголівки:

Версія, яка також візуалізує проміжні кроки:

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

Вихід:

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=

Тести:

Тестові рядки виходять із прикладу запитання про приклади статті Вікіпедії для 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.";

Тестовий вихід:

TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZSO=

YW55IGNhcm5hbCBwbGVhc3VyZSO=

YW55IGNhcm5hbCBwbGVhc3VyZB==

YW55IGNhcm5hbCBwbGVhc3Vy

YW55IGNhcm5hbCBwbGVhc3F=

YW55IGNhcm5hbCBwbGVhcD==

cGxlYXN1cmUu

bGVhc3VyZSO=

ZWFzdXJlLC==

YXN1cmUu

c3VyZSO=

Питання було роз'яснено, щоб вимагати RFC 2045, тому вам потрібно додати трохи коду, щоб розділити вихід на 76-шарові фрагменти та приєднатися до \r\n.
Пітер Тейлор

@PeterTaylor: Дякую, я оновив відповідь на RFC 2045.
Хайко Обердік

браво за цю дуже повну відповідь. Включення обов'язкових перерв рядків (вказавши "RFC 2045" в ОП) насправді була помилкою, ви можете насправді проігнорувати цю частину. Вибачте :)
xem

1

Пітон, 234 символів

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:]

Питання було роз'яснено, щоб вимагати RFC 2045, тому вам потрібно додати трохи коду, щоб розділити вихід на 76-шарові фрагменти та приєднатися до \r\n.
Пітер Тейлор

@PeterTaylor: виправлено.
Кіт Рендалл

1

GolfScript, 80 (77) байт

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

Вищенаведене вмістить рівно 76 символів у рядку, крім останнього рядка. Усі рядки закінчуються CRLF.

Зауважте, що RFC 2045 визначає змінну, максимальна довжина рядка 76 символів, тому ціною гарного виводу ми можемо зберегти 3 додаткові байти.

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

Вищенаведене буде друкувати один символ на рядок, за винятком останнього рядка, який може містити 0, 1 або 2 =символів. GolfScript також додасть остаточний LF, який, згідно з RFC 2045, повинен бути ігнорований програмою декодування.

Приклад

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

Як це працює

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

1

PHP , 200 байт

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

Спробуйте в Інтернеті!

Ви можете замінити рядок ("$s+/=")масивомarray_merge(range(A,Z),range(a,z),range(0,9),["+","/","="])

Тільки для порівняння, який кількість байтів може досягати із недозволеною вбудованою

PHP , 45 байт

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

Спробуйте в Інтернеті!


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}

Якщо ваш браузер не підтримує ES6, ви можете спробувати цю версію (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])повертає "Y2Fmw6k=".


Де код, щоб розділити його на 76-шарові фрагменти, приєднані \r\n?
Пітер Тейлор

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

Дещо незворушний:

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

Приклад :

Вбудований модуль base64 Python використовується лише в цьому прикладі, щоб гарантувати, що eфункція має правильний вихід, сама eфункція не використовує її.

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

Питання було роз'яснено, щоб вимагати RFC 2045, тому вам потрібно додати трохи коду, щоб розділити вихід на 76-шарові фрагменти та приєднатися до \r\n.
Пітер Тейлор

@PeterTaylor виправлено.
Тоні Елліс

0

Желе , 38 байт

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

Спробуйте в Інтернеті!

Оскільки (майже) кожна інша відповідь охоплює вимогу RFC2045 "щонайменше 76 символів на рядок із закінченням рядка \r\n", я дотримувався цього.

Як це працює

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"

Базова декомпресія може бути використана тут, але ṃØbṙ1¤трохи задовга для простої операції.
користувач202729

Можливо, варто попросити Деніса зробити атом, що обертається на основі декомпресії.
користувач202729

Невдачі для 0,0,0.
користувач202729
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.