Base64 uzunluk hesaplaması?


155

Base64 okuduktan sonra wiki ...

Formülün nasıl çalıştığını anlamaya çalışıyorum:

Uzunluğunda bir dize verildiğinde n, base64 uzunluğuresim açıklamasını buraya girin

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

%4==0Kod çözücünün orijinal metin uzunluğunun ne olduğunu bilmesine izin vermek için base64 uzunluğunun olması gerektiğini zaten biliyorum .

Bir sekans için maksimum dolgu sayısı =veya olabilir ==.

wiki: Giriş baytı başına çıkış baytı sayısı yaklaşık 4 / 3'tür (% 33 ek yük)

Soru:

Nasıl çıktı uzunluğunda yerleşmek Yukarıdaki bilgiler yapar resim açıklamasını buraya girin?

Yanıtlar:


210

Her karakter 6 biti (log2(64) = 6 ) .

Bu nedenle temsil etmek için 4 karakter kullanılır 4 * 6 = 24 bits = 3 bytes .

Yani 4*(n/3)temsil etmek için karakterlere ihtiyacınız varn baytları ve bunun 4'ün katlarına yuvarlanması gerekiyor.

4'ün katlarına yuvarlamadan kaynaklanan kullanılmayan dolgu karakterlerinin sayısı açıkça 0, 1, 2 veya 3 olacaktır.


dolgu buraya nereden geliyor?
Royi Namir

1
Bir bayt girişiniz olup olmadığını düşünün. Bu dört karakter çıktı üretecektir. Ancak girişi kodlamak için yalnızca iki çıkış karakteri gereklidir. Yani iki karakter dolgulu olacak.
David Schwartz

2
Çıkış uzunluğu her zaman 4'ün katlarına yuvarlanır, böylece 1, 2 veya 3 giriş baytı => 4 karakter; 4, 5 veya 6 giriş baytı => 8 karakter; 7, 8 veya 9 giriş baytı => 12 karakter.
Paul R

5
Tüm bunları yukarıdaki cevapta açıkladım: (i) her çıkış karakteri 6 bit girişini temsil eder , (ii) 4 çıkış karakteri bu nedenle 4 * 6 = 24 bit'i temsil eder , (iii) 24 bit 3 bayttır , (iv) 3 bayt bu nedenle girişin 4 karakterlik çıktısıyla sonuçlanır , (v) çıktı karakterlerinin girdi baytlarına oranı 4 / 3'tür.
Paul R

2
@ techie_28: 20 * 1024 bayt için 27308 karakter yapıyorum, ama bu sabah henüz kahve içmedim.
Paul R

60

4 * n / 3 unpadded uzunluğu verir.

Ve dolgu için 4'ün en yakın katına yuvarlayın ve 4'ün gücü 2 olduğu için bitsel mantıksal işlemleri kullanabilir.

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

1
Haklısın! -> 4 * n / 3 unpadded uzunluğu verir! yukarıdaki cevaplar doğru değil. -> ((4 * n / 3) + 3) & ~ 3 doğru sonucu döndürür
Cadburry

Pencerenin API CryptBinaryToStringA için girdi olarak çalışmaz.
TarmoPikaro

kabuk kullanan insanlar için heceleyerek:$(( ((4 * n / 3) + 3) & ~3 ))
starfry

1
4 * n / 3zaten başarısız n = 1, bir bayt iki karakter kullanılarak kodlanır ve sonuç açıkça bir karakterdir.
Maarten Bodewes

1
@Crog n = 1 ise yazıldığından tamsayılar kullanarak 4/3 = 1 elde edersiniz. Belirttiğiniz gibi, beklenen sonuç 1 değil, 2'dir.
Maarten Bodewes

25

Referans olarak, Base64 kodlayıcının uzunluk formülü aşağıdaki gibidir:

Base64 kodlayıcının uzunluk formülü

Dediğiniz gibi, nbayt veri verilen bir Base64 kodlayıcı bir 4n/3Base64 karakter dizesi oluşturur . Başka bir deyişle, her 3 baytlık veri 4 Base64 karakteriyle sonuçlanacaktır. EDIT : Bir yorum doğru benim önceki grafik dolgu için hesap vermediğini gösterir; doğru formül Ceiling(4n/3) .

Wikipedia makalesi, ASCII dizesinin örneğinde Man Base64 dizesine nasıl kodlandığını tam olarak gösterir TWFu. Giriş dizesi 3 bayt veya 24 bit boyutundadır, bu nedenle formül, çıktının 4 bayt (veya 32 bit) uzunluğunda olacağını doğru şekilde tahmin eder:TWFu . İşlem, her 6 bit veriyi 64 Base64 karakterinden birine kodlar, böylece 24-bit giriş 6'ya bölünür 4 Base64 karakteri ile sonuçlanır.

Bir yorumda kodlamanın boyutunun ne 123456olacağını sorarsınız . Bu dizenin her karakterinin 1 bayt veya 8 bit olduğunu (ASCII / UTF8 kodlaması varsayarsak), 6 bayt veya 48 bit veri kodladığımızı unutmayın. Denkleme göre çıkış uzunluğunun olmasını bekliyoruz (6 bytes / 3 bytes) * 4 characters = 8 characters.

123456Bir Base64 kodlayıcıya koymak MTIzNDU2, beklediğimiz gibi 8 karakter uzunluğunda oluşturur .


5
Bu formülü kullanarak, doldurulmuş uzunluğu vermediğini unutmayın. Böylece daha uzun bir uzunluğa sahip olabilirsiniz.
Spilarix

Base64 metninden beklenen kodu çözülmüş baytları hesaplamak için formülü kullanıyorum floor((3 * (length - padding)) / 4). Aşağıdaki özü kontrol edin .
Kurt Vangraefschepe

13

tamsayılar

Genellikle iki katını kullanmak istemiyoruz çünkü kayan nokta opsunu, yuvarlama hatalarını vb. Kullanmak istemiyoruz.

Bunun için tavan bölümünün nasıl yapılacağını hatırlamak iyi bir fikirdir: ceil(x / y)çiftler olarak (x + y - 1) / y(negatif sayılardan kaçınırken, taşmalara karşı dikkatli olun) olarak yazılabilir .

Okunabilir

Okunabilirlik için giderseniz, elbette bu şekilde de programlayabilirsiniz (Java'da, örneğin C için makroları kullanabilirsiniz):

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

inlined

Yastıklı

Her 3 bayt (veya daha az) için 4 karakter bloğuna ihtiyacımız olduğunu biliyoruz. Böylece formül olur (x = n ve y = 3 için):

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

veya kombine:

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

derleyiciniz optimize eder 3 - 1, bu yüzden okunabilirliği korumak için böyle bırakın.

unpadded

Daha az yaygın olan unpadded varyantı, çünkü bunun her birinin her 6 bit için yuvarlatılmış bir karaktere ihtiyacımız olduğunu hatırlarız:

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

veya kombine:

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

ancak yine de ikiye bölebiliriz (eğer istiyorsak):

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

okunamayan

Derleyicinizin sizin için son optimizasyonları yapmasına güvenmiyorsanız (veya meslektaşlarınızı karıştırmak istiyorsanız):

Yastıklı

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

unpadded

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

İşte buradayız, iki mantıksal hesaplama yöntemi ve gerçekten istemedikçe herhangi bir şubeye, bit-op veya modulo op'a ihtiyacımız yok.

Notlar:

  • Bir boş sonlandırma baytı eklemek için hesaplamalara 1 eklemeniz gerekebilir.
  • Mime için olası satır sonlandırma karakterlerine ve benzerlerine dikkat etmeniz gerekebilir (bunun için başka cevaplar arayın).

5

Verilen cevaplar orijinal sorunun noktasını kaçırdığını düşünüyorum, bu da belirli bir uzunluk n baytlık bir ikili dize için base64 kodlamasına uyacak ne kadar alan ayrılması gerektiğidir.

Cevap (floor(n / 3) + 1) * 4 + 1

Buna dolgu ve sonlandırıcı bir boş karakter dahildir. Tamsayı aritmetiği yapıyorsanız kat çağrısına ihtiyacınız olmayabilir.

Dolgu dahil olmak üzere, bir base64 dizesi, herhangi bir kısmi parçalar da dahil olmak üzere orijinal dizenin her üç baytlık yığını için dört bayt gerektirir. Dize sonunda fazladan bir veya iki bayt dolgu eklendiğinde hala base64 dizesinde dört bayta dönüştürülür. Çok özel bir kullanımınız yoksa, dolguya, genellikle eşit bir karakter eklemek en iyisidir. C'deki boş bir karakter için fazladan bir bayt ekledim, çünkü bu olmadan ASCII dizeleri biraz tehlikeli ve dize uzunluğunu ayrı olarak taşımanız gerekir.


5
Formülünüz yanlış. N = 3 olduğunu düşünün, beklenen sonuç (null dolgu olmadan) 4'tür, ancak formülünüz 8
değerini

5
Ayrıca, sıfır sonlandırıcıyı dahil etmenin aptalca olduğunu düşünüyorum, özellikle .net hakkında konuştuğumuzdan beri.
CodesInChaos

CryptBinaryToStringA kullanarak pencerelerde doğru çalışır. Bunun için oyum.
TarmoPikaro

5

İşte kodlanmış Base 64 dosyasının orijinal boyutunu KB olarak bir String olarak hesaplamak için bir işlev:

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

Herkes cebirsel formülleri tartışırken, bana söylemek için sadece BASE64'ü kullanmayı tercih ederim:

$ 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

Yani 4 temel64 karakterle temsil edilen 3 bayt formülü doğru görünüyor.


1
Hesaplamalar 1 ns ve bir veya iki kayıt halinde gerçekleştirilebilirken çok fazla bellek ve CPU zamanı gerektiren hesaplamalara karşı bir şeyim var.
Maarten Bodewes

Bilinmeyen miktarda ikili veriyle uğraşmaya çalıştığınızda - bu nasıl yardımcı olur?
İngiltere

Soru, base64'ün kendisini yapmadan çıktı boyutunu hesaplamaya yardımcı olan formüllerle ilgilidir . Bu cevap bazı durumlarda yararlı olsa da, bu soruya yardımcı olmaz.
Alejandro

3

(Kısa ve özlü bir türev verme çabasıyla.)

Her giriş baytının 8 biti vardır, bu nedenle n giriş baytı için şunları elde ederiz:

n × 8 giriş biti

Her 6 bit bir çıkış baytıdır, yani:

tavan ( n × 8/6 ) =  tavan ( n × 4/3 ) çıkış baytı

Bu dolgu olmadan.

Dolgu ile, bunu dörtten fazla çıktı baytına yuvarlıyoruz:

tavan ( tavan ( n × 4/3 ) / 4) × 4 =  tavan ( n × 4 / 3/4 ) × 4 =  tavan ( n / 3) × 4 çıkış baytı

Yuvalanmış Bölümlere bakınİlk denklik için (Wikipedia).

Tam sayı aritmetiği kullanılarak, tavan ( n / m ) ( n + m - 1) div m olarak hesaplanabilir , bu nedenle:

( n * 4 + 2) div 3 dolgusuz

( n + 2) dolgu ile div 3 * 4

Gösterim için:

 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

Son olarak, MIME Base64 kodlaması durumunda, bir son satırsonu gerekip gerekmediğine bağlı olarak yukarı veya aşağı yuvarlanan her 76 çıkış baytı için iki ek bayt (CR LF) gerekir.


Ayrıntılı Analiz için teşekkürler
P Satish Patro

2

Bana doğru formülün olması gerektiği gibi geliyor:

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

Ascii sıfır dolgusu dikkate alınmaz - Windows'ta çalışmaz. (CryptBinaryToStringA)
TarmoPikaro

1

Eğer n% 3 sıfır değilse, bunun kesin bir cevap olduğuna inanıyorum.

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

Mathematica sürümü:

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

İyi eğlenceler

GI


1

Javascript'te basit uygulama

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

1

C konuşan herkes için şu iki makroya bir göz atın:

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

Buradan alındı .


1

Basitleştirilmiş formülü diğer yanıtlarda görmüyorum. Mantık kapsanıyor, ancak yerleşik kullanımım için en temel bir form istedim:

  Unpadded = ((4 * n) + 2) / 3

  Padded = 4 * ((n + 2) / 3)

NOT: Unpadded sayısını hesaplarken tamsayı bölümünü yuvarlıyoruz, yani bu durumda +2 olan Divisor-1'i ekliyoruz


0

Pencerelerde - mime64 boyutlu tampon boyutunu tahmin etmek istedim, ancak tüm kesin hesaplama formülleri benim için çalışmadı - nihayet böyle yaklaşık formülle sonuçlandım:

Mine64 dize ayırma boyutu (yaklaşık) = (((4 * ((ikili tampon boyutu) + 1)) / 3) + 1)

Yani son +1 - ascii-zero için kullanılır - son karakterin sıfır sonunu depolamak için ayrılması gerekir - ama neden "ikili arabellek boyutu" + 1 - Bazı mime64 sonlandırma karakteri olduğundan şüpheleniyorum? Veya bu bir hizalama sorunu olabilir.


0

JS'de @Pedro Silva çözümünü elde etmekle ilgilenen biri varsa, bunun için aynı çözümü kullandım:

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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.