将无符号16位整数存储到文件的最有效方法
我正在使用字典最大大小为64000的字典压缩器。因此,我将我的条目存储为16位整数。
我正在做什么:要编码’a’,我得到它的ASCII值,97,然后将此数字转换为16位整数97的字符串表示。所以我最终编码’0000000001100001’为’a ‘,这显然不会在短期内节省太多空间。
我知道这个算法的更有效版本将从较小的整数大小开始(在我们需要更多的情况下存储的位数较少),但我想知道是否有更好的方法来
-
将我的整数’97’转换为固定长度的ASCII字符串,可以存储16位数据(97将是x位,46347也将是x位)
-
写入只能存储1和0的文件。 因为实际上,似乎我在文本文件中写了16个ascii字符,每个字符都是8位……所以这并不能真正帮助这个原因,是吗?
如果我能以任何方式更清楚,请告诉我。 我对这个网站很新。 谢谢!
编辑:就我所知,我如何存储我的字典完全取决于我。 我只知道我需要能够轻松地读回编码文件并从中获取整数。
另外,我只能包含我为该程序编写的stdio.h,stdlib.h,string.h和头文件。
请忽略这些建议您“直接写入文件”的人。 这有很多问题,最终属于“整数表示”类别。 似乎有一些令人信服的理由使用fwrite
或者什么不能直接将整数写入外部存储器,这里有一些可靠的事实。
瓶颈是外部存储控制器。 如果您正在编写网络应用程序,请使用网络或网络。 因此,将两个字节写为单个fwrite
或两个不同的fputc
应该大致相同的速度,只要你的内存配置文件适合你的平台。 您可以使用setvbuf
调整FILE *
使用的缓冲区数量(注意:必须是2的幂),因此我们总是可以根据我们的分析器告诉我们的内容对每个平台进行微调,尽管这些信息应该是可能通过温和的建议优雅地漂浮在标准库的上游,对其他项目也很有用 。
今天的计算机之间的基础整数表示不一致。 假设您使用使用32位整数和大端表示的系统X将unsigned int
直接写入文件,您最终会遇到在系统Y上读取该文件的问题,该文件使用16位整数和小端表示或系统Z使用具有混合字节表示的64位整数和32个填充位。 如今,我们拥有15年前的这些电脑组合,让人们对ARM big.Little SoC,智能手机和智能电视,游戏机和PC进行折磨,所有这些都有自己的怪癖,超出了标准C的范围,特别是关于整数表示,填充等。
C的开发考虑了抽象,允许您可移植地表达您的算法,因此您不必为每个操作系统编写不同的代码! 下面是一个读取并将四个hex数字转换为unsigned int
值的示例,可移植:
unsigned int value; int value_is_valid = fscanf(fd, "%04x", &value) == 1; assert(value_is_valid); // #include /* NOTE: Actual error correction should occur in place of that * assertioon */
我应该指出为什么我选择%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
值占用两个字节,这就是我如何使用big endian表示来读取这两个字节:
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)
也许你对小端更感兴趣。 整洁的是,代码真的没那么不同。 这是输入:
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#位。