Base 64 Encoding丢失数据
Base 64 Encoding Losing data
这是我第四次尝试base64编码。我的第一次尝试工作,但它不是标准的。它也非常慢!!我使用矢量和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
主要的编码问题是您没有考虑到不是6位的倍数的数据。在这种情况下,最终的4
被转换为0100
而不是010000
,因为没有更多的位可读。您应该填充0
s。
像这样更改Copy
后,最终编码字符是Q
,而不是原来的E
。
std::string data = Str.substr(FirstChar, Count);
while(data.size() < Count) data += '0';
return data;
此外,您添加填充=
的逻辑似乎是关闭的,因为在这种情况下它添加了太多的=
。
关于速度的评论,我主要关注的是如何减少std::string
的使用。考虑到可以使用位操作符直接读取源数据,目前将数据转换为包含0和1的字符串的方式效率非常低。
我不确定我是否可以轻松地提出一种较慢的Base-64转换方法。
代码需要4个头文件(在Mac OS X 10.7.5和g++ 4.7.1上)和编译器选项-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字节长的倍数(因为三个8位字节被编码成四个字节,每个字节包含6位原始数据)。这立即告诉我们有问题。您应该只获得零个,一个或两个pad (=
)字符;从来没有三个。这也证实有问题。
删除两个pad字符留下一个有效的Base-64字符串。当我使用自己的自制Base-64编码和解码函数来解码(截断)输出时,它给了我:
Base64:
0x0000: SU1HLjY3Nyo2MDE=
Binary:
0x0000: 49 4D 47 2E 36 37 37 2A 36 30 31 00 IMG.677*601.
因此,看起来您已经编码了结束字符串的null。当我编码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
字符通常是'='
,但也可以是' '
,因为我使用的系统有不需要填充的古怪想法(在我参与代码之前,它使用非标准字母来引导),代码处理非标准和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] = ' ';
if (datalen == 2)
encode_doublet(bin_data, b64_data, pad);
else if (datalen == 1)
encode_singlet(bin_data, b64_data, pad);
b64_data[4] = ' ';
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()
函数来提供' '
。这些错误有些随意,但与代码有关。
从这里得到的要点是,你应该做位操作,并为给定的输入构建一个4字节的整数倍的字符串。
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;
}
- 防止主数据类型C++的隐式转换
- 用于访问容器<T>数据成员的正确 API
- 嵌套在类中时无法设置成员数据
- 使用流处理接收到的数据
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 在cuda线程之间共享大量常量数据
- C++将文本文件中的数据读取到结构数组中
- 如何在C++中序列化结构数据
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 通过套接字[TCP]传输数据 如何在C / C ++中打包多个整数并使用send() recv()传输数据
- 在c代码之间共享数据的最佳方式
- 链表,反向函数,数据结构
- 数据成员SFINAE的C++17测试:gcc vs clang
- C++浮点数据类型和字符串数据类型无法子到模板函数中
- 如何对点云数据进行排序
- 从矢量<无符号字符>转换为字符* 包括垃圾数据
- 尝试通过OCI例程从Oracle获取blob数据,但出现错误:ORA-01008:并非所有变量都绑定
- Cuda C++:设备上的Malloc类,并用来自主机的数据填充它
- Base 64 Encoding丢失数据