Base64の長さの計算?


155

base64 wikiを読んだ後...

私は式がどのように機能しているを理解しようとしています

文字列の長さがの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になります。


パディングはどこにありますか?
Royi Namir

1
1バイトの入力があるかどうかを検討します。これにより、4文字の出力が生成されます。ただし、入力をエンコードするために必要な出力文字は2つだけです。したがって、2つの文字がパディングされます。
David Schwartz

2
出力長は常に4の倍数に切り上げられるため、1、2、または3入力バイト=> 4文字です。4、5、または6入力バイト=> 8文字。7、8、または9入力バイト=> 12文字。
ポールR

5
これらすべてを上記の回答で説明しました:(i)各出力文字は6 ビットの入力を表し、(ii)4出力文字は4 * 6 = 24 ビットを表します、(iii)24 ビットは3 バイトです、(iv)3 バイトです入力従って4つのもたらす文字(V)出力の比、出力の文字を入力するバイト従って4/3である
ポールR

2
@ techie_28:20 * 1024バイトで27308文字にしていますが、今朝はまだコーヒーを飲んでいません。
Paul R

60

4 * n / 3 パディングされていない長さを与えます。

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

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

1
あなたが正しいです!-> 4 * n / 3はパディングされていない長さを与えます!上記の答えは正しくありません。->((4 * n / 3)+ 3)&〜3は正しい結果を返します
Cadburry

ウィンドウのAPI CryptBinaryToStringAの入力としては機能しません。
TarmoPikaro 2016年

シェルを使用している人のためにそれを説明するには:$(( ((4 * n / 3) + 3) & ~3 ))
starfry

1
4 * n / 3は既にで失敗しn = 1、1バイトは2文字を使用してエンコードされ、結果は明らかに1文字です。
Maarten Bodewes 2017

1
@Crog n = 1の場合は書き留められるため、整数を使用して4/3 = 1になります。あなたが示されてきたように、期待される結果は2、ない1です
マールテンBodewes

25

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

Base64エンコーダーの長さの式

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

Wikipediaの記事は、ASCII文字ManTWFuがその例でどのようにBase64文字列にエンコードされたかを正確に示しています。入力文字列のサイズは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文字の長され、。


5
この式を使用すると、パディングされた長さが得られないことに注意してください。したがって、長さを長くすることができます。
Spilarix 2016

base64テキストから予想されるデコードされたバイトを計算するには、式を使用しfloor((3 * (length - padding)) / 4)ます。次の要点を確認してください。
カートVangraefschepe

13

整数

浮動小数点演算や丸め誤差などを使用したくないので、一般的には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の場合、行末文字などを処理する必要がある場合があります(そのための他の回答を探してください)。

5

与えられた答えは、元の質問の要点、つまり長さnバイトの与えられたバイナリ文字列のbase64エンコーディングに適合するために割り当てられる必要があるスペースの量を欠いていると思います。

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

これには、パディングと終端のnull文字が含まれます。整数演算を行う場合は、floor呼び出しは必要ありません。

埋め込みを含め、base64文字列は、部分的なチャンクを含め、元の文字列の3バイトのチャンクごとに4バイトを必要とします。文字列の最後に追加された1バイトまたは2バイトは、パディングが追加されると、base64文字列の4バイトに変換されます。特別な用途がない限り、パディングを追加するのが最善です。通常は等号です。Cのnull文字用に1バイト追加しました。これがないASCII文字列は少し危険であり、文字列の長さを個別に運ぶ必要があるためです。


5
あなたの式は間違っています。、(ヌルパディング無し)期待される結果を考慮してN = 3~4であるが、数式戻る8
CodesInChaos

5
特にここで.netについて話しているので、nullターミネータを含めるのはばかげていると私は思います。
CodesInChaos 14年

CryptBinaryToStringAを使用して、ウィンドウで正しく動作します。これに対する私の投票。
TarmoPikaro 2016年

5

エンコードされた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;
}

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

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


1
1 nsと1つまたは2つのレジスタで計算を実行できる一方で、大量のメモリとCPU時間を必要とする計算に対して何かがあります。
Maarten Bodewes 2017

未知の量のバイナリデータを処理しようとしている場合、これはどのように役立ちますか?
UKMonkey 2017年

問題は、base64自体実行せずに出力サイズ計算するのに役立つ数式についてです。この回答は状況によっては役立ちますが、この質問には役立ちません。
アレハンドロ

3

(簡潔であるが完全な派生を与える試みで。)

すべての入力バイトは8ビットなので、n入力バイトの場合、次のようになります。

n ×8入力ビット

6ビットごとが出力バイトなので、次のようになります。

ceiln ×8/6)=  ceiln ×4/3)出力バイト

これはパディングなしです。

パディングを使用して、4の倍数の出力バイトに切り上げます。

ceilceiln ×4/3)/ 4)×4 =  ceiln ×4/3/4)×4 =  ceiln / 3)×4出力バイト

最初の等価性については、ネストされた分割(Wikipedia)を参照してください。

整数演算を使用すると、ceiln / mn + 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エンコーディングの場合、76出力バイトごとに2つの追加バイト(CR LF)が必要であり、終端の改行が必要かどうかに応じて切り上げまたは切り下げられます。


詳細な分析に感謝
Pサティシュパトロ

2

私には、正しい式は次のようになるはずです:

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

Asciiゼロフィルは考慮されません-Windowsでは機能しません。(CryptBinaryToStringA)
TarmoPikaro 2016年

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]

楽しんで

GI


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を話すすべての人は、次の2つのマクロを見てください。

// 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)

注:パディングされていないカウントを計算するときは、整数除算を切り上げます。つまり、この場合は+2である除数1を追加します。


0

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

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

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


0

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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.