Расчет длины базы64?

После прочтения base64 wiki...

Я пытаюсь выяснить, как работает эта формула:

Для строки длиной n длина base64 будет enter image description here

Что есть: 4*Math.Ceiling(((double)s.Length/3)))

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

Максимальное количество дополнений для последовательности может быть = или ==.

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

Вопрос:

Как соответствует приведенной выше информации с выходной длиной enter image description here?

Ответ 1

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

Поэтому для обозначения 4 * 6 = 24 bits = 3 bytes используется 4 символа.

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

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

Ответ 2

4 * n / 3 дает неупорядоченную длину.

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

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

Ответ 3

Для справки, формула длины кодировщика Base64 выглядит следующим образом:

Формула длины кодировщика Base64

Как вы сказали, кодер Base64, заданный n байтами данных, выдает строку из 4n/3 символов Base64. Другими словами, каждые 3 байта данных приведут к 4 символам Base64. EDIT: комментарий правильно указывает, что мой предыдущий рисунок не учитывал заполнение; правильная формула Ceiling(4n/3).

В статье Wikipedia показано, как строка 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 создает MTIzNDU2 длиной 8 символов, как и ожидалось.

Ответ 4

Целые

Как правило, мы не хотим использовать двойники, потому что мы не хотим использовать операторы с плавающей запятой, ошибки округления и т.д. Они просто не нужны.

Для этого неплохо запомнить, как выполнять разделение потолка: 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

Unreadable

Если вы не доверяете своему компилятору делать окончательную оптимизацию для вас (или если вы хотите запутать своих коллег):

проложенный

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

без ведущего

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

Итак, мы имеем два логических способа расчета, и нам не нужны никакие ветки, бит-ops или modulo ops - если мы действительно этого не хотим.

Примечания:

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

Ответ 5

Я думаю, что эти ответы не совпадают с исходным вопросом, а именно, сколько пространства должно быть выделено для соответствия кодировке base64 для данной двоичной строки длиной n байтов.

Ответ (floor(n / 3) + 1) * 4 + 1

Это включает в себя заполнение и завершающий нулевой символ. Вам может не понадобиться вызов пола, если вы выполняете целочисленную арифметику.

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

Ответ 6

Вот функция, чтобы вычислить исходный размер закодированного файла Base 64 как String в KB:

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

Ответ 7

Мне кажется, что правильная формула должна быть:

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

Ответ 8

В то время как все остальные обсуждают алгебраические формулы, я бы скорее просто использовал 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 байтов, представленная четырьмя символами base64, выглядит правильно.

Ответ 9

Я считаю, что это точный ответ, если n% 3 не равен нулю, no?

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

Версия Mathematica:

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

Удачи

GI

Ответ 10

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

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

Итак, последний +1 - используется для ascii-zero - последний символ должен быть назначен для хранения нулевого окончания, но почему "размер двоичного буфера" равен + 1 - я подозреваю, что есть какой-то символ окончания mime64? Возможно, это проблема выравнивания.

Ответ 11

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

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

Ответ 12

Если кто-то заинтересован в достижении решения @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
}

Ответ 13

Для всех людей, которые говорят на 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) 

Взято из здесь.