1

これは、base64 エンコーディングを行う 4 回目の試みです。私の最初の試みは機能しますが、標準ではありません。また、非常に遅いです!!! ベクトルと push_back を使用し、多くの消去を行いました。

だから私はそれを書き直すことにしました。これははるかに高速です! データが失われることを除いて。--- ピクセル バッファを圧縮し、圧縮された文字列を base64 でエンコードしているため、可能な限りの速度が必要です。私はZLibを使用しています。画像は 1366 x 768 です。

私はオンラインで見つけたコードをコピーしたくありません。なぜなら、私は自分で何かを書くのが好きで、著作権について心配したり、コード全体にさまざまなソースからの大量のクレジットを入れたりするのが好きではないからです..

とにかく、私のコードは次のとおりです。とても短くシンプルです。

const static std::string Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

inline bool IsBase64(std::uint8_t C)
{
    return (isalnum(C) || (C == '+') || (C == '/'));
}

std::string Copy(std::string Str, int FirstChar, int Count)
{
    if (FirstChar <= 0)
        FirstChar = 0;
    else
        FirstChar -= 1;
    return Str.substr(FirstChar, Count);
}

std::string DecToBinStr(int Num, int Padding)
{
    int Bin = 0, Pos = 1;
    std::stringstream SS;
    while (Num > 0)
    {
        Bin += (Num % 2) * Pos;
        Num /= 2;
        Pos *= 10;
    }
    SS.fill('0');
    SS.width(Padding);
    SS << Bin;
    return SS.str();
}

int DecToBinStr(std::string DecNumber)
{
    int Bin = 0, Pos = 1;
    int Dec = strtol(DecNumber.c_str(), NULL, 10);

    while (Dec > 0)
    {
        Bin += (Dec % 2) * Pos;
        Dec /= 2;
        Pos *= 10;
    }
    return Bin;
}

int BinToDecStr(std::string BinNumber)
{
    int Dec = 0;
    int Bin = strtol(BinNumber.c_str(), NULL, 10);

    for (int I = 0; Bin > 0; ++I)
    {
        if(Bin % 10 == 1)
        {
            Dec += (1 << I);
        }
        Bin /= 10;
    }
    return Dec;
}

std::string EncodeBase64(std::string Data)
{
    std::string Binary = std::string();
    std::string Result = std::string();

    for (std::size_t I = 0; I < Data.size(); ++I)
    {
        Binary += DecToBinStr(Data[I], 8);
    }

    for (std::size_t I = 0; I < Binary.size(); I += 6)
    {
        Result += Base64Chars[BinToDecStr(Copy(Binary, I, 6))];
        if (I == 0) ++I;
    }

    int PaddingAmount = ((-Result.size() * 3) & 3);
    for (int I = 0; I < PaddingAmount; ++I)
        Result += '=';

    return Result;
}

std::string DecodeBase64(std::string Data)
{
    std::string Binary = std::string();
    std::string Result = std::string();

    for (std::size_t I = Data.size(); I > 0; --I)
    {
        if (Data[I - 1] != '=')
        {
            std::string Characters = Copy(Data, 0, I);
            for (std::size_t J = 0; J < Characters.size(); ++J)
                Binary += DecToBinStr(Base64Chars.find(Characters[J]), 6);
            break;
        }
    }

    for (std::size_t I = 0; I < Binary.size(); I += 8)
    {
        Result += (char)BinToDecStr(Copy(Binary, I, 8));
        if (I == 0) ++I;
    }

    return Result;
}

私はこのように上記を使用しています:

int main()
{
    std::string Data = EncodeBase64("IMG." + ::ToString(677) + "*" + ::ToString(604));  //IMG.677*604
    std::cout<<DecodeBase64(Data);        //Prints IMG.677*601
}

上記でわかるように、間違った文字列が出力されます。かなり近いのですが、なぜか 4 が 1 になっています。

今私がする場合:

int main()
{
    std::string Data = EncodeBase64("IMG." + ::ToString(1366) + "*" + ::ToString(768));  //IMG.1366*768
    std::cout<<DecodeBase64(Data);        //Prints IMG.1366*768
}

正しく印刷されます..何が起こっているのか、どこから調べればよいのかわかりません。

誰かが興味を持っていて、私の他の試み (遅いもの) を見たいと思っている場合に備えて: http://pastebin.com/Xcv03KwE

誰かが物事をスピードアップするか、少なくとも私のコードの何が問題なのかを理解することに光を当ててくれることを本当に望んでいます:l

4

3 に答える 3

2

Base-64 変換を行うためのより遅い方法を簡単に思いつくことができるかどうかはわかりません。

このコードには、4 つのヘッダー (G++ 4.7.1 を使用する Mac OS X 10.7.5 の場合) と、受け入れられる-std=c++11ようにするためのコンパイラ オプションが必要です。#include <cstdint>

#include <string>
#include <iostream>
#include <sstream>
#include <cstdint>

ToString()また、定義されていない関数も必要です。私が作成しました:

std::string ToString(int value)
{
    std::stringstream ss;
    ss << value;
    return ss.str();
}

main()関数を使用するコードはToString()少し奇妙です。なぜ単純に を使用するのではなく、断片から文字列を構築する必要があるの"IMG.677*604"でしょうか?

また、中間結果を出力する価値があります。

int main()
{
    std::string Data = EncodeBase64("IMG." + ::ToString(677) + "*" + ::ToString(604));
    std::cout << Data << std::endl;
    std::cout << DecodeBase64(Data) << std::endl;        //Prints IMG.677*601
}

これにより、次の結果が得られます。

SU1HLjY3Nyo2MDE===
IMG.677*601

出力文字列 ( SU1HLjY3Nyo2MDE===) の長さは 18 バイトです。有効な Base-64 でエンコードされた文字列は 4 バイトの倍数でなければならないため、これは間違っているはずです (3 つの 8 ビット バイトは、それぞれが元のデータの 6 ビットを含む 4 バイトにエンコードされるため)。これにより、問題があることがすぐにわかります。ゼロ、1 つ、または 2 つのパッド ( =) 文字のみを取得する必要があります。3つもありません。これは、問題があることも確認しています。

埋め込み文字を 2 つ削除すると、有効な Base-64 文字列が残ります。自作の Base-64 エンコードおよびデコード関数を使用して (切り捨てられた) 出力をデコードすると、次のようになります。

Base64:
0x0000: SU1HLjY3Nyo2MDE=
Binary:
0x0000: 49 4D 47 2E 36 37 37 2A 36 30 31 00               IMG.677*601.

したがって、文字列を終了するヌルをエンコードしたようです。をエンコードするIMG.677*604と、得られる出力は次のとおりです。

Binary:
0x0000: 49 4D 47 2E 36 37 37 2A 36 30 34                  IMG.677*604
Base64: SU1HLjY3Nyo2MDQ=

あなたはコードを高速化したいと言います。正しくエンコードされるように修正することは別として (私はデコードについては詳しく調べていません)、行うすべての文字列操作を避けたいと思うでしょう。これは、文字列操作の演習ではなく、ビット操作の演習である必要があります。

トリプレット、ダブレット、シングレットをエンコードするために、コードに 3 つの小さなエンコード ルーチンがあります。

/* Encode 3 bytes of data into 4 */
static void encode_triplet(const char *triplet, char *quad)
{
    quad[0] = base_64_map[(triplet[0] >> 2) & 0x3F];
    quad[1] = base_64_map[((triplet[0] & 0x03) << 4) | ((triplet[1] >> 4) & 0x0F)];
    quad[2] = base_64_map[((triplet[1] & 0x0F) << 2) | ((triplet[2] >> 6) & 0x03)];
    quad[3] = base_64_map[triplet[2] & 0x3F];
}

/* Encode 2 bytes of data into 4 */
static void encode_doublet(const char *doublet, char *quad, char pad)
{
    quad[0] = base_64_map[(doublet[0] >> 2) & 0x3F];
    quad[1] = base_64_map[((doublet[0] & 0x03) << 4) | ((doublet[1] >> 4) & 0x0F)];
    quad[2] = base_64_map[((doublet[1] & 0x0F) << 2)];
    quad[3] = pad;
}

/* Encode 1 byte of data into 4 */
static void encode_singlet(const char *singlet, char *quad, char pad)
{
    quad[0] = base_64_map[(singlet[0] >> 2) & 0x3F];
    quad[1] = base_64_map[((singlet[0] & 0x03) << 4)];
    quad[2] = pad;
    quad[3] = pad;
}

これは、ネイティブの C++ イディオムを使用するのではなく、C コードとして記述されていますが、示されているコードは C++ でコンパイルする必要があります (ソースの他の場所にある C99 初期化子とは異なります)。配列は文字列base_64_map[]に対応しますBase64Chars。渡されるpad文字は通常は ですが'='、私が使用'\0'しているシステムには、パディングを必要としないという奇抜なアイデアがあり (コードへの関与よりも前の日付で、非標準のアルファベットを使用して起動します)、コードが両方を処理するため、可能性があります。非標準およびRFC 3548標準。

運転コードは次のとおりです。

/* Encode input data as Base-64 string.  Output length returned, or negative error */
static int base64_encode_internal(const char *data, size_t datalen, char *buffer, size_t buflen, char pad)
{
    size_t outlen = BASE64_ENCLENGTH(datalen);
    const char *bin_data = (const void *)data;
    char *b64_data = (void *)buffer;

    if (outlen > buflen)
        return(B64_ERR_OUTPUT_BUFFER_TOO_SMALL);
    while (datalen >= 3)
    {
        encode_triplet(bin_data, b64_data);
        bin_data += 3;
        b64_data += 4;
        datalen -= 3;
    }
    b64_data[0] = '\0';

    if (datalen == 2)
        encode_doublet(bin_data, b64_data, pad);
    else if (datalen == 1)
        encode_singlet(bin_data, b64_data, pad);
    b64_data[4] = '\0';
    return((b64_data - buffer) + strlen(b64_data));
}

/* Encode input data as Base-64 string.  Output length returned, or negative error */
int base64_encode(const char *data, size_t datalen, char *buffer, size_t buflen)
{
    return(base64_encode_internal(data, datalen, buffer, buflen, base64_pad));
}

base64_pad定数は'='; 代わりにbase64_encode_nopad()提供する関数もあります。'\0'エラーはやや恣意的ですが、コードに関連しています。

これを回避するための主なポイントは、ビット操作を行い、特定の入力に対して 4 バイトの正確な倍数である文字列を構築する必要があるということです。

于 2012-12-31T05:46:09.677 に答える
2

エンコードに関する主な問題は、6 ビットの倍数ではないデータを考慮していないことです。この場合、読み取るビットがもうないため、代わりにファイナル4が変換されています。sでパディングすることになっています。01000100000

このように変更した後Copy、最終的にエンコードされた文字はQ、元の ではなく ですE

std::string data = Str.substr(FirstChar, Count);
while(data.size() < Count) data += '0';
return data;

また、この場合は=あまりにも多くのパディングを追加しているため、パディングを追加するためのロジックがオフになっているようです。=

速度に関するコメントに関しては、主に の使用を減らすことに焦点を当てますstd::string。データを 0 と 1 の文字列に現在変換している方法は、ソースがビット単位の演算子で直接読み取られる可能性があることを考えると、かなり非効率的です。

于 2012-12-31T05:41:30.133 に答える
1
std::string EncodeBase64(std::string Data)
{
    std::string Binary = std::string();
    std::string Result = std::string();

    for (std::size_t I = 0; I < Data.size(); ++I)
    {
        Binary += DecToBinStr(Data[I], 8);
    }

    if (Binary.size() % 6)
    {
        Binary.resize(Binary.size() + 6 - Binary.size() % 6, '0');
    }

    for (std::size_t I = 0; I < Binary.size(); I += 6)
    {
        Result += Base64Chars[BinToDecStr(Copy(Binary, I, 6))];
        if (I == 0) ++I;
    }

    if (Result.size() % 4)
    {
        Result.resize(Result.size() + 4 - Result.size() % 4, '=');
    }

    return Result;
}
于 2012-12-31T05:45:24.640 に答える