Base64 길이 계산?


155

base64 위키를 읽은 후 ...

수식이 어떻게 작동 하는지 알아 내려고합니다 .

길이가 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 바이트의 입력이 있는지 고려하십시오. 그러면 네 개의 출력 문자가 생성됩니다. 그러나 입력을 인코딩하려면 두 개의 출력 문자 만 필요합니다. 따라서 두 문자가 패딩됩니다.
David Schwartz

2
출력 길이는 항상 4의 배수로 반올림되므로 1, 2 또는 3 개의 입력 바이트 => 4 자; 4, 5 또는 6 개의 입력 바이트 => 8 자; 7, 8 또는 9 개의 입력 바이트 => 12 자
Paul R

5
위의 답변 에서이 모든 것을 설명했습니다 : (i) 각 출력 문자 는 6 비트 의 입력을 나타냅니다 . (ii) 4 개의 출력 문자 는 4 * 6 = 24 비트를 나타냅니다 . (iii) 24 비트 는 3 바이트입니다 . (iv) 3 바이트 따라서 입력의 결과는 4 문자 의 출력이되며, (v) 출력 문자 대 입력 바이트 의 비율은 4 / 3입니다.
Paul 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

쉘을 사용하는 사람들을 위해 철자를 쓰는 법 :$(( ((4 * n / 3) + 3) & ~3 ))
starfry

1
4 * n / 3에서 이미 실패한 n = 1경우 1 바이트는 두 문자를 사용하여 인코딩되며 결과는 분명히 한 문자입니다.
Maarten Bodewes

1
@Crog n = 1이면 기록되므로 정수를 사용하여 4/3 = 1이됩니다. 표시된 바와 같이 예상 결과는 1이 아니라 2입니다.
Maarten Bodewes

25

참고로 Base64 인코더의 길이 공식은 다음과 같습니다.

Base64 엔코더 길이 공식

말했듯이, n바이트 단위의 데이터가 주어진 Base64 인코더 는 4n/3Base64 문자 의 문자열을 생성합니다 . 다시 말해, 3 바이트의 데이터마다 4 개의 Base64 문자가 생성됩니다. 편집 : 주석은 이전 그래픽이 패딩을 설명하지 않았다고 올바르게 지적합니다. 올바른 공식은 Ceiling(4n/3) 입니다.

Wikipedia 기사는 ASCII 문자열 이 예제에서 Man Base64 문자열로 인코딩되는 방식을 정확하게 보여줍니다 TWFu. 입력 문자열의 크기는 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.

123456Base64 인코더에 넣으면 MTIzNDU2예상대로 8 자 길이가됩니다.


5
이 수식을 사용하면 채워진 길이가 제공되지 않습니다. 따라서 더 긴 길이를 가질 수 있습니다.
Spilarix

base64 텍스트에서 예상되는 디코딩 된 바이트를 계산하기 위해 formula를 사용합니다 floor((3 * (length - padding)) / 4). 다음 요점을 확인하십시오 .
커트 Vangraefschepe

13

정수

일반적으로 우리는 부동 소수점 연산, 반올림 오류 등을 사용하지 않기 때문에 복식을 사용하고 싶지 않습니다. 그것들은 필요하지 않습니다.

이를 위해 상한 나눗셈을 수행하는 방법을 기억하는 것이 좋습니다. 복수 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

그러나 여전히 원하는 경우 두 개로 나눌 수 있습니다.

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

읽을 수 없음

컴파일러가 자신을 위해 최종 최적화를 수행한다고 신뢰하지 않는 경우 (또는 동료를 혼동시키려는 경우) :

패딩

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

패딩되지 않은

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

따라서 우리는 두 가지 논리적 계산 방법이 있으며, 실제로 원하지 않는 한 분기, 비트 연산 또는 모듈로 연산이 필요하지 않습니다.

노트:

  • 분명히 널 종료 바이트를 포함하기 위해 계산에 1을 추가해야 할 수도 있습니다.
  • Mime의 경우 가능한 줄 종결 문자 등을 관리해야 할 수도 있습니다 (다른 답변 찾기).

5

주어진 답변이 원래 질문의 요점을 놓친 것 같습니다. 이는 길이 n 바이트의 주어진 이진 문자열에 대해 base64 인코딩에 맞게 얼마나 많은 공간을 할당해야하는지입니다.

정답은 (floor(n / 3) + 1) * 4 + 1

여백 및 종료 널 문자가 포함됩니다. 정수 산술을 수행하는 경우 플로어 호출이 필요하지 않을 수 있습니다.

패딩을 포함하여 base64 문자열에는 부분 청크를 포함하여 원래 문자열의 3 바이트 청크마다 4 바이트가 필요합니다. 패딩이 추가 될 때 문자열 끝에 추가 된 1 바이트 또는 2 바이트는 여전히 base64 문자열에서 4 바이트로 변환됩니다. 매우 구체적으로 사용하지 않는 한 일반적으로 등호 인 패딩을 추가하는 것이 가장 좋습니다. 이것을 사용하지 않는 ASCII 문자열은 약간 위험하므로 문자열 길이를 별도로 운반해야하기 때문에 C에서 null 문자에 여분의 바이트를 추가했습니다.


5
수식이 잘못되었습니다. n = 3을 고려하면 (널 패딩이없는) 예상 결과는 4이지만 수식은 8을 반환합니다.
CodesInChaos

5
또한 null 터미네이터를 포함하는 것은 어리석은 것으로 생각합니다. 특히 .net에 대해 이야기하고 있기 때문입니다.
코드 InChaos

CryptBinaryToStringA를 사용하여 Windows에서 올바르게 작동합니다. 이것에 대한 나의 투표.
TarmoPikaro

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 및 하나 또는 두 개의 레지스터에서 수행 할 수있는 동안 많은 메모리와 CPU 시간이 필요한 계산에 대해 뭔가가 있습니다.
Maarten Bodewes

알 수없는 양의 이진 데이터를 처리하려고 할 때 어떻게 도움이됩니까?
UKMonkey

문제는 base64 자체 수행 하지 않고 출력 크기를 계산하는 데 도움이되는 수식에 관한 것입니다. 이 답변은 일부 상황에서 유용하지만이 질문에는 도움이되지 않습니다.
Alejandro

3

(간결하면서도 완전한 파생을 제공하려는 시도에서)

모든 입력 바이트는 8 비트이므로 n 입력 바이트의 경우 다음을 얻습니다.

n × 8 입력 비트

6 비트마다 출력 바이트이므로

ceil ( n × 8/6 ) =  ceil ( n × 4/3 ) 출력 바이트

패딩이 없습니다.

패딩을 사용하면 4 배의 출력 바이트로 반올림합니다.

ceil ( ceil ( n × 4 / 3) / 4) × 4 =  ceil ( n × 4 / 3 / 4) × 4 =  ceil ( n / 3) × 4 출력 바이트

첫 번째 동등성에 대해서는 중첩 부서 (Wikipedia)를 참조하십시오 .

정수 산술을 사용하여 ceil ( n / m )( n + 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 Satish Patro

2

올바른 수식은 다음과 같아야합니다.

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

Ascii zero fill은 고려되지 않습니다-Windows에서는 작동하지 않습니다. (CryptBinaryToStringA)
TarmoPikaro

1

나는 이것이 n % 3이 0이 아니라면 정확한 답이라고 믿는다.

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

Mathematica 버전 :

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

즐기세요

미군 병사


1

자바 스크립트에서 간단한 구현

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

1

C를 사용하는 모든 사람들에게 다음 두 가지 매크로를 살펴보십시오.

// 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 인 Divisor-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.