開發中遇到存儲圖片文件於數據庫這樣的需求。
我們知道文件本身就是一份二進制數據,不同類型的文件只是編碼形式不同,對應的解讀形式不同,無論txt文件、jpg文件亦或是mp4文件,本質上都是0和1組成的。而在C++中我們可以通過字節流來讀寫文件,也就是說我們可以把文件讀入char數組中,也可以將char數組中的數據寫入為文件。
開發中需要將圖片存在數據庫中,每個文件幾KB,想將之存在數據庫表的varchar類型中。大小不是問題,大的話就以text類型存儲。關鍵是,C++讀取的文件字節流的char數組是不能直接存儲的,因為里面會含有很多‘\0’。如果按照字符串來理解,就是結束符了。那樣一來存的內容就不完整了。
所以需要將文件字節流的char數組 轉化為二進制字符串,再存儲數據庫。
從數據庫讀取后,再轉化回文件字節流的char數組。
每一個char,其實就是一個字節,也就是8bit,每個bit是0或者1。我要做的就是讀取每一位,將之轉化為字符的'0'或者'1',再存儲。
所以如果原本是N字節的文件,轉化后大小為N*8。
下面是我文件字節流與二進制字符串相互轉換的測試程序,參考了幾篇文章,里面的程序我略有修改:
http://blog.csdn.net/k346k346/article/details/52170623
http://blog.csdn.net/pointer_y/article/details/51525679
將一個名為timg.jpg的文件,放在程序目錄下,程序會將之讀取,轉化為二進制字符串,然后再轉化回去,寫入為文件timg2.jpg。
運行結束后會生成一個timg2.jpg,且和timg.jpg一模一樣。
該程序還會作 字符串轉化的測試,效果為:
#include "stdafx.h" #include "iostream" #include "string.h" #include <fstream> #include <assert.h> using namespace std; #define P1LEN 10013 #define P2LEN 80104 /** * 設置一個字符第n位bit值 * 2018-02-06 */ int bit_set(unsigned char *p_data, unsigned char position, int flag) { int i = 0; assert(p_data); if (position > 8 || position < 1 || (flag != 0 && flag != 1)) return -1; if (flag != (*p_data >> (position - 1) & 1)) *p_data ^= 1 << (position - 1); return 0; } /** * 字符數組 與 二進制字符串 相互轉化 * 2018-02-06 */ int char_array_2_binary_str(char ch_buf[],int ch_len,char binary_char_buf[],int binary_char_len,int type){ if(type!=0&&type!=1) return -1; //字節流轉二進制字符流 if(type==0){ if(binary_char_len < 8*ch_len) return -1; int char_buf_index=0; for(int i=0;i<ch_len;++i){ for(int j=7;j>=0;--j){ if(ch_buf[i]>>j&0x1) binary_char_buf[char_buf_index++]='1'; else binary_char_buf[char_buf_index++]='0'; } } return 0; } if (type == 1) { if(8*ch_len < binary_char_len) return -1; char tmp_str[9]=""; int ret_buf_index=0; for(int i=0;i<binary_char_len;i=i+8){ strncpy(tmp_str, binary_char_buf+i, 8); for(int j=0; j<8; ++j){ if (tmp_str[j] == '0') bit_set((unsigned char *)ch_buf+ret_buf_index, 8-j, 0); if (tmp_str[j] == '1') bit_set((unsigned char *)ch_buf+ret_buf_index, 8-j, 1); } ret_buf_index++; } return 0; } } int _tmain(int argc, _TCHAR* argv[]) { ////////首先,字符串轉換測試//////////////// char* txt = "異度神劍"; cout << "原始字符串為:"<< txt << endl << endl; char binary_txt_buf[65]; char_array_2_binary_str(txt,8,binary_txt_buf,64,0); binary_txt_buf[64] = '\0'; cout << "轉化后的二進制字符串為:" << binary_txt_buf << endl << endl; char txt2[9]; char_array_2_binary_str(txt2,8,binary_txt_buf,64,1); txt2[8] = '\0'; cout << "轉化回原始字符串為:" <<txt2 << endl << endl; ///////字符串轉換測試結束///////////////// ///////文件轉換測試/////////////////////// const char * filename = "timg.jpg"; const char * filename2 = "timg2.jpg"; ifstream infile(filename,ios::in|ios::binary); if(!infile) abort( ); // 存儲圖片的char數組空間 char pic_buf[P1LEN]; infile.read(pic_buf,P1LEN); infile.close(); // 轉換成2進制字符串的存儲空間 char ret_buf[P2LEN]; char_array_2_binary_str(pic_buf,P1LEN,ret_buf,P2LEN,0); // 轉換回來 char pic_buf2[P1LEN]; char_array_2_binary_str(pic_buf2,P1LEN,ret_buf,P2LEN,1); ofstream outfile(filename2,ios::out|ios::binary); if(!outfile) abort( ); // 寫入文件,生成timg2.jpg outfile.write(pic_buf2,P1LEN); outfile.close(); ///////文件轉換測試結束/////////////////////// return 0; }
本來需求就是存儲圖片於數據庫,理論上已經實現了。現在我想到字符串和二進制的轉化應該是很有趣的。比如以后我寫文章:1101001011101100101101101100100011001001111100011011110110100011
你能看懂什么意思嗎?
但我用自己程序轉化一下,就看懂了。
以后可以寫個帶界面的字符串和二進制的轉化程序。
就可以很方便地對自己寫的日記作加密了。
到時我會將代碼上傳到github,或可期待開個新坑……