2

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

我目前在做什么: 要编码'a',我得到它的ASCII值97,然后将此数字转换为16位整数97的字符串表示形式。所以我最终为'a'编码'0000000001100001' ',这在短期内显然不会节省太多空间。

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

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

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

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

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

另外,我只能包含我为程序编写的 stdio.h、stdlib.h、string.h 和头文件。

4

2 回答 2

1

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

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

当今计算机之间的基础整数表示是不一致的。假设您unsigned int使用使用 32 位整数和大端表示的系统 X 直接将 s 写入文件,您最终会在使用 16 位整数和小端表示的系统 Y 或系统 Z 上读取该文件时遇到问题使用具有混合字节序表示和 32 个填充位的 64 位整数。如今,我们拥有这种 15 年前的计算机组合,人们用 ARM big.Little 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值占用两个字节,这就是我如何使用大端表示法读取这两个字节的方式:

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);

对于大于两个字节的任何内容(即 along 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.

好吧,这就是我今天对这篇文章的更新……社交延迟的极客现在退出。

于 2013-03-20T19:17:39.897 回答
0

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

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

然后,当您阅读文件时,您知道您的数字位于每 x # 位,因此您只需将它们逐个或以大块的形式读出,然后将这些块放入一个类型的数组中与 x # 位匹配。

于 2013-03-20T18:38:26.090 回答