将无符号16位整数存储到文件的最有效方法

时间:2013-03-20 17:00:08

标签: c dictionary binary compression ascii

我在C中使用字典最大大小64000制作字典压缩器。因此,我将我的条目存储为16位整数。

我目前正在做的事情:  为了编码“A”,我得到它的ASCII值,97,然后这个数字转换成97的16位整数的字符串表示所以我最终编码“0000000001100001”的“A”,这显然是不在短期内节省了大量空间。

我知道这个算法的更高效版本将从较小的整数大小开始(直到我们需要更多的存储位数较少),但我想知道是否有更好的方法

  1. 将我的整数'97'转换为固定长度的ASCII字符串,可存储16位数据(97位为x位,46347也位于x位)

  2. 写入只能存储1和0的文件。因为实际上,我似乎正在为一个文本文件写入16个ascii字符,每个字符都是8位......所以这并不能真正帮助这个原因,是吗?

  3. 如果我能以任何方式更清楚,请告诉我。我对这个网站很新。谢谢!

    编辑:就我所知,我如何存储我的字典完全取决于我。我只知道我需要能够轻松地读回编码文件并从中获取整数。

    另外,我可以包括我为该程序编写的stdio.h,stdlib.h,string.h和头文件。

2 个答案:

答案 0 :(得分:1)

请忽略这些建议您“直接写入文件”的人。这方面存在许多问题,最终属于“整数表示”类别。似乎有一些令人信服的理由使用fwrite直接将整数写入外部存储,或者什么不是,这里有一些可靠的事实。

瓶颈是外部存储控制器。如果您正在编写网络应用程序,请使用该网络或网络。因此,将两个字节写为单个fwrite或两个不同的fputc,应该大致相同的速度,只要您的内存配置文件适合您的平台。您可以使用FILE *调整setvbuf使用的缓冲区数量(注意:必须是2的幂),这样我们就可以根据我们的分析器对每个平台进行微调告诉我们,虽然这些信息可能优雅地漂浮在标准库的上游,通过温和的建议也可以用于其他项目

基础整数表示在今天的计算机之间不一致。假设您使用使用32位整数和大端表示的系统X将unsigned int直接写入文件,您将结束在使用16位整数和小端表示的系统Y上读取该文件的问题,或者使用具有混合端表示和32填充位的64位整数的系统Z。如今,我们拥有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而不是%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 unsignedlong 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的参数中引起任何类型的修改,写入或全局状态更改。 /强>

嗯,那是我今天对这篇文章的更新......社交推迟极客们现在退出。

答案 1 :(得分:0)

您正在考虑的是使用ASCII字符来保存您的号码,这是完全没必要且效率最低的。

最节省空间的方法(不使用复杂算法)只是将数字的字节转储到文件中(位数必须取决于您打算保存的最大数量。或者有8位,16位等多个文件。

然后,当你读到文件时,你知道你的数字是按x位数来定位的,所以你只需要逐个读取它们或者在一个大块中读出它们然后只需将这些块放入一个数组中匹配x#位的类型。