Розрахунок довжини основи64?


155

Прочитавши вікі base64 ...

Я намагаюся зрозуміти, як працює формула:

Враховуючи рядок довжиною n, довжина base64 будевведіть тут опис зображення

Який є : 4*Math.Ceiling(((double)s.Length/3)))

Я вже знаю, що довжина base64 повинна бути такою, %4==0щоб декодер знав, якою була початкова довжина тексту.

Максимальна кількість прокладки для послідовності може бути =або ==.

wiki: Кількість вихідних байтів на один вхідний байт приблизно 4/3 (33% накладні витрати)

Питання:

Як інформація вище узгоджується з вихідною довжиною введіть тут опис зображення?

Відповіді:


210

Кожен символ використовується для представлення 6 біт ( log2(64) = 6).

Тому 4 символи використовуються для представлення 4 * 6 = 24 bits = 3 bytes.

Отже, вам потрібні 4*(n/3)символи для представлення nбайтів, і це потрібно округлити до кратного 4.

Кількість невикористаних символів прокладки в результаті округлення до кратного 4 очевидно буде 0, 1, 2 або 3.


де сюди потрапляє підкладка?
Рой Намір

1
Подумайте, чи є у вас один байт введення. Це дасть чотири символи виводу. Але для кодування вводу потрібні лише два вихідних символи. Тож два персонажі будуть набивати.
Девід Шварц

2
Довжина виводу завжди округлена до кратного 4, тому 1, 2 або 3 вхідних байта => 4 символи; 4, 5 або 6 вхідних байтів => 8 символів; 7, 8 або 9 вхідних байтів => 12 символів.
Пол Р

5
Я пояснив усе це у відповіді вище: (i) кожен вихідний графік являє собою 6 біт вхідного сигналу, (ii) 4 вихідні символи, отже, представляють 4 * 6 = 24 біт , (iii) 24 біти - 3 байти , (iv) 3 байти Таким чином, вхід призводить до 4 символів виведення, (v) тому відношення вихідних символів до вхідних байтів становить 4 / 3.
Paul R

2
@ techie_28: Я створюю 27308 символів на 20 * 1024 байти, але я ще не вживав кави цього ранку.
Пол Р

60

4 * n / 3 дає довжину, що не вкладається.

І округніть до найближчого кратного 4 для прокладки, а як 4 - потужність 2, можна використовувати побізні логічні операції.

((4 * n / 3) + 3) & ~3

1
Ти правий! -> 4 * n / 3 дає необмежену довжину! відповіді вище невірні. -> ((4 * n / 3) + 3) & ~ 3 повертає правильний результат
Cadburry

Не працює як вхід для API вікна CryptBinaryToStringA.
ТармоПікаро

щоб $(( ((4 * n / 3) + 3) & ~3 ))
прописати

1
4 * n / 3вже не вдається n = 1, один байт кодується за допомогою двох символів, і результат явно один символ.
Maarten Bodewes

1
@Crog Як записано, якщо n = 1, ви отримаєте 4/3 = 1 за допомогою цілих чисел. Як ви вказали, очікуваний результат 2, а не 1.
Maarten Bodewes

25

Для довідки, формула довжини кодера Base64 така:

Формула довжини кодера Base64

Як ви вже говорили, кодер Base64, що надається nбайтами даних, створить рядок 4n/3символів Base64. По-іншому, кожні 3 байти даних матимуть 4 символи Base64. EDIT : коментар правильно вказує, що моя попередня графіка не враховувала прокладки; правильна формула Ceiling(4n/3) .

Стаття у Вікіпедії точно показує, як рядок ASCII Man закодований у рядок Base64 TWFuу своєму прикладі. Рядок входу 3 байта, або 24 біт, в розмірі, так що формула правильно пророкує вихід буде 4 байта (або 32 біт) довжиною: TWFu. Процес кодує кожні 6 бітів даних в один з 64 символів Base64, тому 24-бітний вхід, розділений на 6, призводить до 4 символів Base64.

Ви запитуєте в коментарі, яким би був розмір кодування 123456. Маючи на увазі, що кожен символ цього рядка має розмір 1 байт або 8 біт (якщо припускати кодування ASCII / UTF8), ми кодуємо 6 байтів або 48 біт даних. Відповідно до рівняння, ми очікуємо, що довжина виходу буде такою (6 bytes / 3 bytes) * 4 characters = 8 characters.

Введення 123456в кодер Base64 створює MTIzNDU28 символів, як ми і очікували.


5
Користуючись цією формулою, пам’ятайте, що вона не дає прокладеної довжини. Так ви можете мати більшу довжину.
Spilarix

Щоб обчислити очікувані декодовані байти з тексту646464, я використовую формулу floor((3 * (length - padding)) / 4). Ознайомтесь із наступною суттю .
Курт Ванграефшепе

13

Цілі особи

Як правило, ми не хочемо використовувати парні, тому що ми не хочемо використовувати операції з плаваючою точкою, помилки округлення тощо. Вони просто не потрібні.

Для цього непогано пам’ятати, як виконати поділ стелі: ceil(x / y)в парних може бути записано як (x + y - 1) / y(при цьому уникайте від’ємних цифр, але остерігайтеся переповнення).

Читабельна

Якщо ви хочете прочитати, ви, звичайно, також можете запрограмувати його так (наприклад, у Java, для C, звичайно, ви можете використовувати макроси):

public static int ceilDiv(int x, int y) {
    return (x + y - 1) / y;
}

public static int paddedBase64(int n) {
    int blocks = ceilDiv(n, 3);
    return blocks * 4;
}

public static int unpaddedBase64(int n) {
    int bits = 8 * n;
    return ceilDiv(bits, 6);
}

// test only
public static void main(String[] args) {
    for (int n = 0; n < 21; n++) {
        System.out.println("Base 64 padded: " + paddedBase64(n));
        System.out.println("Base 64 unpadded: " + unpaddedBase64(n));
    }
}

Нарізний

Набивна

Ми знаємо, що нам потрібно 4 блоки символів на кожен 3 байти (або менше). Тоді формула стає (для x = n і y = 3):

blocks = (bytes + 3 - 1) / 3
chars = blocks * 4

або комбіновано:

chars = ((bytes + 3 - 1) / 3) * 4

ваш компілятор оптимізує 3 - 1, тому просто залиште його так, щоб зберегти читабельність.

Незавантажений

Менш поширений варіант, що не розміщений, для цього ми пам’ятаємо, що кожному нам потрібен символ на кожні 6 біт, закруглені вгору:

bits = bytes * 8
chars = (bits + 6 - 1) / 6

або комбіновано:

chars = (bytes * 8 + 6 - 1) / 6

Однак ми все одно можемо розділити на два (якщо хочемо):

chars = (bytes * 4 + 3 - 1) / 3

Нечитабельна

Якщо ви не довіряєте своєму компілятору зробити остаточну оптимізацію для вас (або якщо ви хочете заплутати своїх колег):

Набивна

((n + 2) / 3) << 2

Незавантажений

((n << 2) | 2) / 3

Таким чином, ми маємо два логічні способи обчислення, і нам не потрібні гілки, розрядні або модульні операції - якщо тільки ми цього не хочемо.

Примітки:

  • Очевидно, вам може знадобитися додати 1 до обчислень, щоб включити нульовий байт завершення.
  • Для Mime вам може знадобитися подбати про можливі символи припинення рядків і подібні (шукайте інші відповіді на це).

5

Я думаю, що наведені відповіді пропускають точку початкового запитання, а саме - скільки місця потрібно виділити для кодування base64 для заданої двійкової рядки довжиною n байтів.

Відповідь така (floor(n / 3) + 1) * 4 + 1

Сюди входить прокладка та закінчуючий нульовий символ. Можливо, вам не знадобиться зворотній дзвінок, якщо ви робите цілу арифметику.

Включаючи прокладку, рядок base64 вимагає чотирьох байтів на кожен трибайтовий фрагмент оригінальної рядки, включаючи будь-які часткові фрагменти. Один або два байти, зайві в кінці рядка, все одно будуть перетворені на чотири байти в рядку base64, коли додано доповнення. Якщо у вас немає специфічного використання, найкраще додати підкладку, як правило, рівний символ. Я додав додатковий байт для нульового символу в C, тому що рядки ASCII без цього є трохи небезпечними, і вам потрібно буде переносити довжину рядка окремо.


5
Ваша формула неправильна. Розглянемо n = 3, очікуваний результат (без нульової прокладки) - 4, але ваша формула повертається 8.
CodesInChaos

5
Я також думаю, що включення нульового термінатора є нерозумним, тим більше, що ми тут говоримо про .net.
CodesInChaos

Правильно працює у Windows, використовуючи CryptBinaryToStringA. Мій голос за це.
ТармоПікаро

5

Ось функція для обчислення вихідного розміру закодованого файлу Base 64 у вигляді рядка в КБ:

private Double calcBase64SizeInKBytes(String base64String) {
    Double result = -1.0;
    if(StringUtils.isNotEmpty(base64String)) {
        Integer padding = 0;
        if(base64String.endsWith("==")) {
            padding = 2;
        }
        else {
            if (base64String.endsWith("=")) padding = 1;
        }
        result = (Math.ceil(base64String.length() / 4) * 3 ) - padding;
    }
    return result / 1000;
}

3

Поки всі інші обговорюють алгебраїчні формули, я скоріше просто використовую сам BASE64, щоб сказати мені:

$ echo "Including padding, a base64 string requires four bytes for every three-byte chunk of the original string, including any partial chunks. One or two bytes extra at the end of the string will still get converted to four bytes in the base64 string when padding is added. Unless you have a very specific use, it is best to add the padding, usually an equals character. I added an extra byte for a null character in C, because ASCII strings without this are a little dangerous and you'd need to carry the string length separately."| wc -c

525

$ echo "Including padding, a base64 string requires four bytes for every three-byte chunk of the original string, including any partial chunks. One or two bytes extra at the end of the string will still get converted to four bytes in the base64 string when padding is added. Unless you have a very specific use, it is best to add the padding, usually an equals character. I added an extra byte for a null character in C, because ASCII strings without this are a little dangerous and you'd need to carry the string length separately." | base64 | wc -c

710

Тож здається, що формула з 3-х байт, представлених 4-ма базовими64 символами, здається правильною.


1
У мене щось проти розрахунків, які вимагають багато пам'яті та часу процесора, тоді як обчислення можна проводити за 1 нс і один або два регістри.
Maarten Bodewes

Тож коли ви намагаєтесь мати справу з невідомими кількістю бінарних даних - як це допомагає?
UKMonkey

Питання стосується формул, які допомагають обчислити розмір вихідного сигналу, не роблячи себе base64. Хоча ця відповідь корисна в деяких ситуаціях, це не допомагає в цьому питанні.
Алехандро

3

(У спробі дати ще короткий, але повний вихід.)

Кожен вхідний байт має 8 біт, тому для n вхідних байтів ми отримуємо:

n × 8 вхідних біт

Кожні 6 біт є вихідним байтом, тому:

ceil ( n × 8/6 ) =  ceil ( n × 4/3 ) вихідних байтів

Це без прокладки.

За допомогою прокладки ми округляємо це до декількох чотирьох вихідних байтів:

ceil ( ceil ( n × 4/3 ) / 4) × 4 =  ceil ( n × 4/3/4 ) × 4 =  ceil ( n / 3) × 4 вихідних байти

Дивіться вкладені відділи (Вікіпедія) для першої еквівалентності.

Використовуючи цілу арифметику, ceil ( n / m ) можна обчислити як ( n + m - 1) div m , отже отримаємо:

( n * 4 + 2) div 3 без прокладки

( n + 2) div 3 * 4 з підкладкою

Для ілюстрації:

 n   with padding    (n + 2) div 3 * 4    without padding   (n * 4 + 2) div 3 
------------------------------------------------------------------------------
 0                           0                                      0
 1   AA==                    4            AA                        2
 2   AAA=                    4            AAA                       3
 3   AAAA                    4            AAAA                      4
 4   AAAAAA==                8            AAAAAA                    6
 5   AAAAAAA=                8            AAAAAAA                   7
 6   AAAAAAAA                8            AAAAAAAA                  8
 7   AAAAAAAAAA==           12            AAAAAAAAAA               10
 8   AAAAAAAAAAA=           12            AAAAAAAAAAA              11
 9   AAAAAAAAAAAA           12            AAAAAAAAAAAA             12
10   AAAAAAAAAAAAAA==       16            AAAAAAAAAAAAAA           14
11   AAAAAAAAAAAAAAA=       16            AAAAAAAAAAAAAAA          15
12   AAAAAAAAAAAAAAAA       16            AAAAAAAAAAAAAAAA         16

Нарешті, у випадку кодування MIME Base64 потрібні два додаткові байти (CR LF) на кожні 76 вихідних байтів, округлені вгору або вниз, залежно від того, чи потрібний завершальний новий рядок.


Дякуємо за детальний аналіз
P Satish Patro

2

Мені здається, що правильною формулою має бути:

n64 = 4 * (n / 3) + (n % 3 != 0 ? 4 : 0)

Ascii нульове заповнення не враховується - не працює в Windows. (CryptBinaryToStringA)
ТармоПікаро

1

Я вважаю, що це точна відповідь, якщо n% 3 не нуль, ні?

    (n + 3-n%3)
4 * ---------
       3

Версія Mathematica:

SizeB64[n_] := If[Mod[n, 3] == 0, 4 n/3, 4 (n + 3 - Mod[n, 3])/3]

Весело

ГІ


1

Просте реалізація в JavaScript

function sizeOfBase64String(base64String) {
    if (!base64String) return 0;
    const padding = (base64String.match(/(=*)$/) || [])[1].length;
    return 4 * Math.ceil((base64String.length / 3)) - padding;
}

1

Для всіх людей, які говорять на C, погляньте на ці два макроси:

// calculate the size of 'output' buffer required for a 'input' buffer of length x during Base64 encoding operation
#define B64ENCODE_OUT_SAFESIZE(x) ((((x) + 3 - 1)/3) * 4 + 1) 

// calculate the size of 'output' buffer required for a 'input' buffer of length x during Base64 decoding operation
#define B64DECODE_OUT_SAFESIZE(x) (((x)*3)/4) 

Взято звідси .


1

Я не бачу спрощеної формули в інших відповідях. Логіка охоплена, але я хотів найосновнішу форму для свого вбудованого використання:

  Unpadded = ((4 * n) + 2) / 3

  Padded = 4 * ((n + 2) / 3)

ПРИМІТКА. Під час обчислення нерозкладеного підрахунку ми округляємо ціле ділення, тобто додаємо Дільник-1, який у цьому випадку становить +2.


0

У Windows - я хотів оцінити розмір буфера розміру mime64, але всі точні формули обчислення для мене не спрацювали - нарешті я закінчив приблизно цю формулу:

Розмір виділення рядка Mine64 (приблизний) = (((4 * ((розмір двійкового буфера) + 1)) / 3) + 1)

Отже, останній +1 - він використовується для ascii-zero - останній символ повинен виділятися для зберігання нульового закінчення - але чому "розмір двійкового буфера" дорівнює + 1 - я підозрюю, що є якийсь символ завершення mime64? Або це може бути певна проблема вирівнювання.


0

Якщо є хтось зацікавлений у досягненні рішення @Pedro Silva в JS, я просто переніс це саме рішення для нього:

const getBase64Size = (base64) => {
  let padding = base64.length
    ? getBase64Padding(base64)
    : 0
  return ((Math.ceil(base64.length / 4) * 3 ) - padding) / 1000
}

const getBase64Padding = (base64) => {
  return endsWith(base64, '==')
    ? 2
    : 1
}

const endsWith = (str, end) => {
  let charsFromEnd = end.length
  let extractedEnd = str.slice(-charsFromEnd)
  return extractedEnd === end
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.