web-dev-qa-db-fra.com

Calcul de la longueur en base64?

Après avoir lu le base64 wiki ...

J'essaie de comprendre comment la formule fonctionne: 

Avec une chaîne de longueur n, la longueur en base64 sera enter image description here

Quel est: 4*Math.Ceiling(((double)s.Length/3)))

Je sais déjà que la longueur en base64 doit être %4==0 pour permettre au décodeur de savoir quelle était la longueur du texte d'origine.

Le nombre maximum de remplissage pour une séquence peut être = ou ==.

wiki: le nombre d'octets de sortie par octet d'entrée est d'environ 4/3 (33% surcharge.)

Question:

Comment les informations ci-dessus s'accordent-elles avec la longueur de sortie enter image description here ?

116
Royi Namir

Chaque caractère est utilisé pour représenter 6 bits (log2(64) = 6). 

Par conséquent, 4 caractères sont utilisés pour représenter 4 * 6 = 24 bits = 3 bytes.

Donc, vous avez besoin de 4*(n/3) caractères pour représenter n octets, qui doit être arrondi à un multiple de 4. 

Le nombre de caractères de remplissage non utilisés résultant de l'arrondissement d'un multiple de 4 sera évidemment égal à 0, 1, 2 ou 3.

156
Paul R

4 * n / 3 donne une longueur non additionnée.

Et arrondissez au multiple de 4 le plus proche pour le remplissage, et comme 4 est une puissance de 2 peut utiliser des opérations logiques au niveau des bits. 

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

Pour référence, la formule de longueur du codeur Base64 est la suivante:

 Base64 encoder's length formula

Comme vous l'avez dit, un encodeur Base64 à l'aide de n octets de données générera une chaîne de caractères 4n/3 Base64. En d'autres termes, tous les 3 octets de données génèrent 4 caractères Base64. EDIT: Un commentaire souligne correctement que mon graphique précédent ne prenait pas en compte le remplissage; la formule correcte étaitCeiling(4n/3).

L'article de Wikipedia montre exactement comment la chaîne ASCII Man est codée dans la chaîne Base64 TWFu dans son exemple. La chaîne d'entrée ayant une taille de 3 octets ou 24 bits, la formule prédit correctement que la sortie aura une longueur de 4 octets (ou 32 bits): TWFu. Le processus code tous les 6 bits de données en un des 64 caractères Base64. L'entrée 24 bits divisée par 6 génère donc 4 caractères Base64.

Vous demandez dans un commentaire quelle serait la taille de l'encodage 123456. En gardant à l'esprit que chaque caractère de cette chaîne a une taille de 1 octet ou 8 bits (en supposant un codage ASCII/UTF8), nous codons 6 octets ou 48 bits de données. Selon l'équation, nous prévoyons que la longueur de sortie sera (6 bytes / 3 bytes) * 4 characters = 8 characters

Mettre 123456 dans un encodeur Base64 crée MTIzNDU2, qui est composé de 8 caractères, exactement comme nous le pensions.

23
David Schwartz

Entiers

En règle générale, nous ne voulons pas utiliser les doublons parce que nous ne voulons pas utiliser les ops à virgule flottante, les erreurs d'arrondi, etc. Ils ne sont tout simplement pas nécessaires.

Pour cela, il est bon de rappeler comment effectuer la division de plafond: ceil(x / y) en double peut être écrit en (x + y - 1) / y (tout en évitant les nombres négatifs, mais méfiez-vous des débordements).

Lisible

Si vous optez pour la lisibilité, vous pouvez bien sûr aussi le programmer comme ceci (par exemple en Java, pour le C, vous pouvez utiliser des macros, bien sûr):

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

En ligne

Rembourré

Nous savons que nous avons besoin de 4 blocs de caractères à la fois pour chaque groupe de 3 octets (ou moins). La formule devient alors (pour x = n et y = 3):

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

ou combiné:

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

votre compilateur optimisera le 3 - 1, donc laissez-le comme ceci pour maintenir la lisibilité.

Non rembourré

La variante non-rembourrée est moins courante. Pour cela, nous rappelons que nous avons besoin d’un caractère pour chaque 6 bits, arrondi à la hausse:

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

ou combiné:

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

on peut cependant quand même diviser par deux (si on veut):

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

Illisible

Si vous ne faites pas confiance à votre compilateur pour effectuer les optimisations finales à votre place (ou si vous voulez confondre vos collègues):

Rembourré

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

Non rembourré

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

Voilà donc deux méthodes logiques de calcul, et nous n’avons pas besoin de branches, de bit-op ou de modulo, à moins que nous ne le voulions vraiment.

Remarques:

  • Évidemment, vous devrez peut-être ajouter 1 aux calculs pour inclure un octet de fin nulle.
  • Pour Mime, vous devrez peut-être vous occuper d’éventuels caractères de fin de ligne et autres (cherchez d’autres réponses à cette question).
7
Maarten Bodewes

Je pense que les réponses données manquent le point de la question initiale, à savoir combien d’espace doit être alloué pour s’adapter au codage base64 pour une chaîne binaire donnée de longueur n octets.

La réponse est (floor(n / 3) + 1) * 4 + 1

Cela inclut un remplissage et un caractère nul final. Vous pouvez ne pas avoir besoin de l'appel au sol si vous utilisez l'arithmétique des nombres entiers.

Y compris le remplissage, une chaîne base64 nécessite quatre octets pour chaque bloc de trois octets de la chaîne d'origine, y compris tous les fragments partiels. Un ou deux octets supplémentaires à la fin de la chaîne seront toujours convertis en quatre octets dans la chaîne base64 lorsque le remplissage est ajouté. À moins que vous n'ayez une utilisation très spécifique, il est préférable d'ajouter le remplissage, généralement un caractère égal à. J'ai ajouté un octet supplémentaire pour un caractère nul en C, parce que les chaînes ASCII sans cela sont un peu dangereuses et que vous auriez besoin de transporter la longueur de la chaîne séparément.

5
Ian Nartowicz

Voici une fonction permettant de calculer la taille d'origine d'un fichier encodé en Base 64 sous forme de chaîne en Ko:

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

Il me semble que la bonne formule devrait être:

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

Je crois que celui-ci est une réponse exacte si n% 3 n'est pas nul, non?

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

Version de Mathematica:

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

S'amuser

GI

1
igerard

Pendant que tout le monde débat des formules algébriques, je préfère utiliser BASE64 lui-même pour me dire: 

$ 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

Il semble donc que la formule de 3 octets représentée par 4 caractères base64 semble correcte.

1
Michael Adams

S'il y a quelqu'un qui est intéressé par la solution @Pedro Silva dans JS, je viens de porter la même solution pour elle:

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

Simple implémentation en javascript

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

Dans Windows - je voulais estimer la taille du tampon mime64, mais toutes les formules de calcul précises ne fonctionnaient pas pour moi - j'ai finalement obtenu une formule approximative comme celle-ci:

Mine64 taille d'allocation de chaînes (approximative) = (((4 * ((taille du tampon binaire) + 1))/3) + 1)

Donc dernier +1 - il est utilisé pour ascii-zero - le dernier caractère doit être alloué à la fin du stockage zéro - mais pourquoi "taille du tampon binaire" vaut + 1 - je soupçonne qu'il existe un caractère de terminaison mime64? Ou peut-être est-ce un problème d'alignement.

0
TarmoPikaro