web-dev-qa-db-de.com

Base64 Längenberechnung?

Nach dem Lesen des base64 wiki ...

Ich versuche, how's die Formel zu ermitteln, die funktioniert: 

Wenn eine Zeichenfolge mit der Länge von n angegeben ist, wird die Länge von base64 angegeben enter image description here

Welches ist: 4*Math.Ceiling(((double)s.Length/3)))

Ich weiß bereits, dass die Länge von base64 %4==0 sein muss, damit der Decoder wissen kann, wie lange der Text ursprünglich war.

Die maximale Anzahl der Auffüllungen für eine Sequenz kann = oder == sein.

wiki: Die Anzahl der Ausgangsbytes pro Eingangsbyte beträgt ungefähr 4/3 (33% Overhead).

Frage:

Wie die obigen Informationen mit der Ausgabelänge übereinstimmen enter image description here ?

116
Royi Namir

Jedes Zeichen wird zur Darstellung von 6 Bits (log2(64) = 6) verwendet. 

Daher werden 4 Zeichen zur Darstellung von 4 * 6 = 24 bits = 3 bytes verwendet.

Sie benötigen also 4*(n/3) chars, um n Bytes darzustellen, und dies muss auf ein Vielfaches von 4 gerundet werden. 

Die Anzahl der nicht verwendeten Auffüllzeichen, die sich aus der Aufrundung auf ein Vielfaches von 4 ergeben, ist offensichtlich 0, 1, 2 oder 3.

156
Paul R

4 * n / 3 gibt nicht aufgefüllte Länge an.

Und für das Auffüllen auf das nächste Vielfache von 4 aufrunden, und als 4 kann eine Potenz von 2 bitweise logische Operationen verwenden. 

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

Als Referenz ist die Längenformel des Base64-Encoders wie folgt:

 Base64 encoder's length formula

Wie Sie gesagt haben, erzeugt ein Base64-Encoder, der n Datenbytes enthält, eine Zeichenfolge aus 4n/3 Base64-Zeichen. Anders ausgedrückt: Alle 3 Datenbytes ergeben 4 Base64-Zeichen. EDIT: Ein Kommentar weist richtig darauf hin, dass meine vorherige Grafik das Auffüllen nicht berücksichtigt hat; die richtige Formel lautetCeiling(4n/3).

Der Wikipedia-Artikel zeigt genau, wie die Zeichenfolge ASCII Man in die Base64-Zeichenfolge TWFu in ihrem Beispiel codiert wurde. Die Eingabezeichenfolge hat eine Größe von 3 Byte oder 24 Bit. Die Formel sagt also richtig voraus, dass die Ausgabe 4 Byte (oder 32 Bit) lang ist: TWFu. Der Prozess codiert alle 6 Datenbits in eines der 64 Base64-Zeichen, sodass der 24-Bit-Eingang, geteilt durch 6, 4 Base64-Zeichen ergibt.

Sie fragen in einem Kommentar, wie groß die Kodierung 123456 wäre. Wenn man bedenkt, dass jedes Zeichen dieser Zeichenfolge 1 Byte oder 8 Bit groß ist (unter der Annahme einer ASCII/UTF8-Kodierung), codieren wir 6 Byte oder 48 Bit Daten. Gemäß der Gleichung erwarten wir, dass die Ausgabelänge (6 bytes / 3 bytes) * 4 characters = 8 characters ist. 

Durch Einfügen von 123456 in einen Base64-Encoder wird MTIzNDU2 erstellt, der genau wie erwartet 8 Zeichen lang ist.

23
David Schwartz

Ganzzahlen

Im Allgemeinen möchten wir keine Doubles verwenden, weil wir keine Gleitkommaoperationen, Rundungsfehler usw. verwenden möchten. Sie sind einfach nicht erforderlich.

Daher ist es eine gute Idee, sich daran zu erinnern, wie die Deckeneinteilung durchgeführt wird: ceil(x / y) in Doubles kann als (x + y - 1) / y geschrieben werden (wobei negative Zahlen vermieden werden, aber der Überlauf ist zu vermeiden).

Lesbar

Wenn Sie sich für die Lesbarkeit entscheiden, können Sie es natürlich auch so programmieren (Beispiel in Java, für C können Sie natürlich Makros verwenden):

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

Inline

gepolstert

Wir wissen, dass wir zu jeder Zeit 4 Zeichenblöcke für jeweils 3 Bytes (oder weniger) benötigen. Dann wird die Formel (für x = n und y = 3):

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

oder kombiniert:

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

ihr Compiler optimiert den 3 - 1, lassen Sie ihn einfach so, um die Lesbarkeit zu erhalten.

nicht gepolstert

Weniger üblich ist die ungepolsterte Variante. Dazu müssen wir uns daran erinnern, dass wir jeweils ein Zeichen für jeweils 6 Bits benötigen, aufgerundet:

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

oder kombiniert:

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

wir können jedoch immer noch durch zwei teilen (wenn wir wollen):

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

Unleserlich

Wenn Sie nicht vertrauen, dass Ihr Compiler die endgültigen Optimierungen für Sie ausführt (oder wenn Sie Ihre Kollegen verwirren möchten):

gepolstert

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

nicht gepolstert

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

Es gibt also zwei logische Berechnungsarten, und wir brauchen keine Verzweigungen, Bitoperationen oder Modulooperationen - es sei denn, wir wollen das wirklich.

Anmerkungen:

  • Offensichtlich müssen Sie den Berechnungen möglicherweise 1 hinzufügen, um ein Null-Beendigungsbyte aufzunehmen.
  • Bei Mime müssen Sie möglicherweise auf mögliche Zeilenabschlusszeichen und dergleichen achten (suchen Sie nach anderen Antworten).
7
Maarten Bodewes

Ich denke, dass die gegebenen Antworten den Punkt der ursprünglichen Frage verfehlen, nämlich wie viel Speicherplatz zugewiesen werden muss, um die base64-Kodierung für eine gegebene binäre Zeichenfolge der Länge n Bytes anzupassen.

Die Antwort lautet (floor(n / 3) + 1) * 4 + 1

Dies beinhaltet Auffüllen und ein abschließendes Nullzeichen. Sie benötigen den Floor-Aufruf möglicherweise nicht, wenn Sie eine Ganzzahlarithmetik ausführen.

Einschließlich der Auffüllung erfordert eine base64-Zeichenfolge vier Byte für jeden 3-Byte-Block der ursprünglichen Zeichenfolge, einschließlich aller partiellen Blöcke. Ein oder zwei zusätzliche Bytes am Ende der Zeichenfolge werden immer noch in vier Byte in der base64-Zeichenfolge konvertiert, wenn das Auffüllen hinzugefügt wird. Wenn Sie nicht einen bestimmten Verwendungszweck haben, ist es am besten, die Auffüllung hinzuzufügen, normalerweise ein gleiches Zeichen. Ich habe ein zusätzliches Byte für ein Nullzeichen in C hinzugefügt, weil ASCII - Zeichenfolgen ohne dieses etwas gefährlich sind und Sie die Zeichenfolgenlänge separat tragen müssen.

5
Ian Nartowicz

Hier ist eine Funktion zum Berechnen der Originalgröße einer codierten Base 64-Datei als String in 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;
}
3
Pedro Silva

Es scheint mir, dass die richtige Formel sein sollte:

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

Ich glaube, dass dies eine exakte Antwort ist, wenn n% 3 nicht Null ist, nein?

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

Mathematica-Version:

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

Habe Spaß

GI

1
igerard

Während alle anderen über algebraische Formeln debattieren, benutze ich lieber BASE64, um mir zu sagen: 

$ 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

Es scheint also, dass die Formel von 3 Bytes, die durch 4 Base64-Zeichen dargestellt wird, richtig scheint.

1
Michael Adams

Wenn es jemanden gibt, der daran interessiert ist, die @Pedro Silva-Lösung in JS zu erreichen, habe ich genau diese Lösung dafür portiert:

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
}
0
elverde

Einfache Implementierung in Javascript

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

In Windows - Ich wollte die Größe eines Puffers in der Größe Mime64 schätzen, aber alle genauen Berechnungsformeln funktionierten nicht für mich - schließlich hatte ich eine ungefähre Formel wie diese:

Mine64-Stringzuweisungsgröße (ungefähr) = (((4 * ((binäre Puffergröße) + 1))/3) + 1)

Letztes +1 - es wird für Ascii-Zero verwendet - das letzte Zeichen muss zugewiesen werden, um die Null-Endung zu speichern - aber warum "binäre Puffergröße" ist +1 - Ich vermute, dass es ein Mime64-Beendigungszeichen gibt? Oder es könnte sich um ein Ausrichtungsproblem handeln.

0
TarmoPikaro