web-dev-qa-db-ja.com

Base64の長さの計算?

base64を読んだ後 wiki ...

私はhow's式の働きを理解しようとしています:

長さがnの文字列を指定すると、base64の長さは enter image description here

どちらですか:4*Math.Ceiling(((double)s.Length/3)))

デコーダーが元のテキストの長さを知ることができるようにするため、base64の長さは%4==0でなければならないことは既に知っています。

シーケンスのパディングの最大数は、=または==です。

wiki:入力バイトあたりの出力バイト数は約4/3(オーバーヘッド33%)です

質問:

Howは、上記の情報を出力長で解決します enter image description here ?

123
Royi Namir

各文字は、6ビット(log2(64) = 6)を表すために使用されます。

したがって、4 * 6 = 24 bits = 3 bytesを表すために4文字が使用されます。

したがって、nバイトを表すには4*(n/3) charsが必要であり、これを4の倍数に切り上げる必要があります。

4の倍数に切り上げた結果の未使用のパディング文字の数は、明らかに0、1、2、または3になります。

162
Paul R

4 * n / 3は、パディングなしの長さを提供します。

また、パディングのために4の最も近い倍数に切り上げます。4は2の累乗であるため、ビットごとの論理演算を使用できます。

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

参考までに、Base64エンコーダーの長さの式は次のとおりです。

Base64 encoder's length formula

既に述べたように、nバイトのデータが指定されたBase64エンコーダーは、4n/3 Base64文字の文字列を生成します。言い換えると、データの3バイトごとに4つのBase64文字が生成されます。 EDIT以前のグラフィックではパディングが考慮されていなかったことが正しくコメントされています。正しい式はCeiling(4n/3)です。

ウィキペディアの記事では、ASCII文字列ManがBase64文字列TWFuにどのようにエンコードされているかを正確に示しています。入力文字列のサイズは3バイト(24ビット)なので、式は、出力が4バイト(または32ビット)の長さになることを正しく予測します:TWFu。このプロセスでは、データの6ビットごとに64個のBase64文字の1つにエンコードされるため、24ビット入力を6で割ると4個のBase64文字になります。

エンコード123456のサイズはどうなるかをコメントで尋ねます。その文字列のすべての文字はすべてサイズが1バイトまたは8ビット(ASCII/UTF8エンコードを想定)であることに留意して、6バイトまたは48ビットのデータをエンコードします。方程式によると、出力の長さは(6 bytes / 3 bytes) * 4 characters = 8 charactersになると予想されます。

123456をBase64エンコーダーに入れると、予想通り、8文字のMTIzNDU2が作成されます。

23
David Schwartz

整数

一般に、浮動小数点演算、丸め誤差などを使用したくないため、倍精度を使用したくありません。これらは単に必要ではありません。

このためには、天井除算の実行方法を覚えておくことをお勧めします。doubleの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));
    }
}

インライン

追加済み

3バイト(またはそれ以下)ごとに、一度に4文字のブロックが必要であることを知っています。したがって、式は(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

ただし、2で割ることはできます(必要な場合)。

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

読めない

コンパイラが最終的な最適化を行うことを信頼していない場合(または同僚を混乱させたい場合):

追加済み

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

パッドなし

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

したがって、2つの論理的な計算方法があり、実際に必要な場合を除き、分岐、ビット演算、モジュロ演算は必要ありません。

ノート:

  • 当然、計算に1を追加してヌル終了バイトを含める必要がある場合があります。
  • Mimeの場合、可能性のある行終了文字などを処理する必要がある場合があります(そのための他の回答を探してください)。
7
Maarten Bodewes

与えられた答えは元の質問のポイントを見逃していると思います。それは、長さnバイトの与えられたバイナリ文字列のbase64エンコーディングに適合するためにどれだけのスペースを割り当てる必要があるかです。

答えは(floor(n / 3) + 1) * 4 + 1です

これには、パディングと終端のヌル文字が含まれます。整数演算をしている場合、フロア呼び出しは必要ないかもしれません。

パディングを含めて、base64文字列には、部分的なチャンクを含む、元の文字列の3バイトチャンクごとに4バイトが必要です。文字列の末尾に余分な1バイトまたは2バイトは、パディングが追加されたときにbase64文字列で4バイトに変換されます。非常に特殊な用途がない限り、パディング(通常は等号)を追加するのが最善です。 Cのヌル文字に余分なバイトを追加しました。これがないASCII文字列は少し危険であり、文字列の長さを個別に保持する必要があるためです。

5
Ian Nartowicz

以下は、エンコードされたBase 64ファイルの元のサイズを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;
}
4
Pedro Silva

正しい数式は次のようになっているように思えます。

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

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]

楽しむ

GI

1
igerard

他の誰もが代数公式を議論している間、私は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

したがって、4バイトのbase64文字で表される3バイトの式は正しいようです。

1
Michael Adams

JSで@Pedro Silvaソリューションを実現することに興味のある人がいる場合は、同じソリューションを移植しました。

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

JavaScriptでの簡単な実装

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

Windowsでは-mime64サイズのバッファのサイズを推定したかったのですが、正確な計算式はすべてうまくいきませんでした-最終的にこのような近似式になりました:

Mine64文字列割り当てサイズ(概算)=(((4 *((バイナリバッファーサイズ)+ 1))/ 3)+ 1)

したがって、最後の+1-ascii-zeroに使用されます-最後の文字はゼロの終わりを格納するために割り当てる必要があります-しかし、「バイナリバッファサイズ」が+ 1である理由-mime64終了文字があると思われますか?または、これは何らかのアライメントの問題である可能性があります。

0
TarmoPikaro