[英]Most efficient way to store an unsigned 16-bit Integer to a file
我正在用C制作字典壓縮器,字典最大大小為64000。因此,我將條目存儲為16位整數。
我目前正在做什么:要編碼“ a”,我要得到其ASCII值97,然后將此數字轉換為16位整數97的字符串表示形式。因此我最終為“ a”編碼了“ 0000000001100001” ”,這顯然在短期內不會節省太多空間。
我知道此算法的更有效版本將從較小的整數大小開始(較少的存儲位,直到我們需要更多位)開始,但是我想知道是否有更好的方法可以
將我的整數'97'轉換為固定長度的ASCII字符串,該字符串可以存儲16位數據(97將是x位,46347也將是x位)
寫入只能存儲1和0的文件。 因為實際上是在向文本文件寫入16個ascii字符,每個字符都是8位...所以這實際上並沒有太大幫助,不是嗎?
請以任何方式讓我更加清晰。 我是這個網站的新手。 謝謝!
編輯:據我所知,我如何存儲字典完全取決於我。 我只知道我需要能夠輕松地讀回編碼的文件並從中獲取整數。
另外,我只能包含為該程序編寫的stdio.h,stdlib.h,string.h和頭文件。
請不要理會那些建議您“直接寫入文件”的人。 與此相關的問題很多,最終都屬於“整數表示”類別。 使用fwrite
或what-not將整數直接寫到外部存儲中似乎有一些令人信服的原因,這里有一些可靠的事實。
瓶頸是外部存儲控制器。 如果要編寫網絡應用程序,則為網絡,否則為網絡。 因此,如果您的內存配置文件適合您的平台,那么將兩個字節作為單個fwrite
或作為兩個不同的fputc
寫入應該大致相同的速度。 您可以使用setvbuf
將FILE *
的緩沖區使用量調整到一定程度(注意:必須為2的冪),因此我們總是可以根據分析器告訴我們的每個平台進行微調,盡管此信息應可能會通過溫和的建議優雅地向標准庫上游浮動,從而對其他項目也有用 。
當今的計算機之間底層的整數表示形式是不一致的。 假設您使用系統X將unsigned int
直接寫入文件,該系統使用32位int和大端序表示,那么在系統Y上讀取該文件時會遇到問題,該系統使用16位int和小端序表示或系統Z使用具有混合字節序表示形式的64位整數和32個填充位。 如今,我們混合使用了15年前的計算機,人們大受ARM的折磨。很少有SoC,智能手機和智能電視,游戲機和PC,它們都有自己的怪癖,不屬於標准C領域,特別是關於整數表示,填充等。
C的開發考慮到了抽象性,使您可以輕便地表達算法,從而不必為每個操作系統編寫不同的代碼! 這是一個將四個十六進制數字讀取並轉換為unsigned int
值的示例,可移植:
unsigned int value;
int value_is_valid = fscanf(fd, "%04x", &value) == 1;
assert(value_is_valid); // #include <assert.h>
/* NOTE: Actual error correction should occur in place of that
* assertioon
*/
我應該指出為什么選擇%04X
而不選擇%04X
%08X
或更現代的東西的原因...如果直到今天我們仍然提出一些問題, 不幸的是 , 有些學生使用的教科書和編譯器已經有20多年的歷史了...他們的int
是16位的,而且從技術上講,他們的編譯器在這方面是兼容的(盡管他們確實應該在整個學術界推廣gcc和llvm)。 考慮到可移植性,這是我如何編寫該值的方法:
value &= 0xFFFF;
fprintf(fd, "%04x", value);
// side-note: We often don't check the return value of `fprintf`, but it can also become \
very important, particularly when dealing with streams and large files...
假設您的unsigned int
值占據兩個字節,這是我使用大端字節表示法可移植地讀取這兩個字節的方法:
int hi = fgetc(fd);
int lo = fgetc(fd);
unsigned int value = 0;
assert(hi >= 0 && lo >= 0); // again, proper error detection & handling logic should be here
value += hi & 0xFF; value <<= 8;
value += lo & 0xFF;
...這就是我按照大端順序寫入這兩個字節的方法:
fputc((value >> 8) & 0xFF, fd);
fputc(value & 0xFF, fd);
// and you might also want to check this return value (perhaps in a finely tuned end product)
也許您對Little Endian更感興趣。 整潔的是,代碼確實沒有什么不同。 輸入如下:
int lo = fgetc(fd);
int hi = fgetc(fd);
unsigned int value = 0;
assert(hi >= 0 && lo >= 0);
value += hi & 0xFF; value <<= 8;
value += lo & 0xFF;
...這是輸出:
fputc(value & 0xFF, fd);
fputc((value >> 8) & 0xFF, fd);
對於大於兩個字節的任何內容(即, long unsigned
或long signed
),您可能想要fwrite((char unsigned[]){ value >> 24, value >> 16, value >> 8, value }, 1, 4, fd);
或例如減少樣板的東西。 考慮到這一點,形成預處理器宏似乎並不濫用:
#define write(fd, ...) fwrite((char unsigned){ __VA_ARGS__ }, 1, sizeof ((char unsigned) { __VA_ARGS__ }), fd)
我想一個人可能會這樣看,就像選擇以下兩個弊端中的更好者一樣:預處理程序濫用或上面代碼中的幻數4
,因為現在我們可以write(fd, value >> 24, value >> 16, value >> 8, value);
沒有對4
進行硬編碼...但是一個不為人所知的詞: 副作用可能會引起頭痛,因此請不要在write
參數中引起任何形式的修改,寫入或全局狀態更改。
好吧,這是我今天對此信息的更新...社交延遲的怪胎人員現在退出。
您正在考慮的是利用ASCII字符保存數字,這完全沒有必要,而且效率最低。
最節省空間的方法(不使用復雜算法)是將數字的字節轉儲到文件中(位數必須取決於您要保存的最大數字,或者具有多個文件用於8位,16位等
然后,當您讀取文件時,您就會知道數字位於每x個位數中,因此只需將它們逐個或大塊地讀出,然后將大塊分成一個類型的數組即可匹配x#位。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.