假设Visual C / C ++ 6,我有一个22399个元素的复杂数据结构,如下所示:
{
{ "(SAME", "AS", "U+4E18)", "HILLOCK", "OR", "MOUND"},
{ "TO", "LICK;", {1, 1, 0}, "TASTE,", "A", "MAT,", "BAMBOO", "BARK"},
{ "(J)", "NON-STANDARD", "FORM", "OF", "U+559C", ",", {1, 1, 0}, "LIKE,", "LOVE,", "ENJOY;", {1, 1, 4}, "JOYFUL", "THING"},
{ "(AN", "ANCIENT", {1, 2, 2}, {1, 2, 3}, "U+4E94)", "FIVE"},
...
}
宣布这个的最佳方法是什么?我尝试过像
这样的事情char * abbrevs3[22399][] = { ... };
和
char * abbrevs3[22399][][] = { ... };
但是编译会产生一些长期的东西。
编辑:数据是某些Unihan字符描述的数据库。我一直在探索压缩数据的各种方法。目前你有22399个条目,每个条目可能包含不同数量的字符串,或者是{abbrev marker,最后一次看到的行,最后一次看到的那行的元素}的三元组。
顺便说一句Greg的说法,我可能需要让每一行包含相同数量的元素,即使其中一些是空字符串。是这样的吗?
编辑#2 :我发现三元组中的某些数值超出了char的限制。
答案 0 :(得分:4)
我会考虑以XML或其他结构化形式存储数据,然后读取和解析它而不是在代码中进行初始化。您在初始化时付出的代价将不仅仅是为了便于理解和提高代码的可维护性。我还考虑设计一个特定的数据结构来保存每个条目。
[编辑]以下示例尝试复制您的后续说明:
enum EntryType { string = 0, triple = 1 };
typedef struct {
enum EntryType entry_type;
union {
char** string;
int[3] *triple;
}
} Entry;
typedef struct {
Entry *entries;
} Abbreviation;
Abbreviation *abbrevs3;
abbrevs3 = parseAbbreviationData("path-to-abbreviations/abbrevs.xml");
答案 1 :(得分:3)
在C中,您只能在声明数组时忽略第一个维度:
char * abbrevs3[][22399] = { ... };
这是因为编译器想要知道每个“行”的大小,以便它可以正确地布置“列”。我将尺寸放在引号中,因为您可以自由地以任何方式解释尺寸,但这是二维数组的通常惯例。
尽管如此,目前尚不清楚您的数据结构实际上是什么,或者您尝试将其初始化为什么。您的示例数据似乎没有任何模式。
答案 2 :(得分:2)
我刚刚阅读了你的新帖子并重新阅读了原帖,我想我完全理解了这里的目标。对不起,花了这么长时间,我有点慢。
要解释这个问题,请在原始示例的第4行:
{ "(AN", "ANCIENT", {1, 2, 2}, {1, 2, 3}, "U+4E94)", "FIVE"},
您希望将三元组转换为对先前使用的字符串的引用,以尝试压缩数据。该行变为:
{ "(AN", "ANCIENT", "FORM", "OF", "U+4E94)", "FIVE"},
如果目标是压缩,我认为你不会在这里获得太大的收益。自引用三元组每个都是3个字节,但是被替换的字符串总共只有8个字节,计算空终止符,并且您只在此行上保存2个字节。这就是使用字符。由于你的结构太大而你需要使用int作为引用,你的三元组实际上是12字节,这更糟糕。在这种情况下,您只需要替换12个字符或更多字符的单词来节省空间。
如果我完全不在这里,那么请随意忽略我,但我认为对空格进行标记然后删除重复单词的方法只是一个穷人的Huffman compression。霍夫曼字母表是longest common substrings的列表,或其他一些标准文本压缩方法可能适用于此问题。
如果由于某种原因这不是一个选项,我想我会得到你的数据中所有独特单词的列表,并将其用作查找表。然后将所有字符串作为索引列表存储到该表中。你必须使用两个表,但最后它可能更简单,它可以节省你现在用作“缩写标记”的前导1所使用的空间。基本上,您的缩写标记将成为单个索引而不是三元组。
所以,
const char * words[] = {
"hello", "world", "goodbye", "cruel"
};
const int strings[] = {
{ 0, 1 },
{ 2, 3, 1 }
};
如果你的琴弦长度大致不一致,你仍然会失去很多空间。
答案 3 :(得分:1)
我认为这里的问题是你是否可以静态声明一个C风格字符串的多维数组,其中每行有不同数量的字符串。所以,像这样:
const char * arr[][3] =
{
{"bla", "bla", "bla"},
{"bla", "bla" }
};
在某些语言中,这被称为“锯齿状阵列”。在C和C ++中你可以这样做,虽然编译器会想要分配空间来存储所有行,就好像它们的长度相同,所以你最终不会初始化第二个数组的第3项。当我在gcc上测试它时,该数组中的第三项设置为NULL,但我不知道你是否可以指望它。
我认为你不能让编译器接受像{1,2,3}那样声明为C样式字符串的数组。即使它确实如此,并且你将它们视为字符串,你也会遇到问题,因为它们不会被终止。
我同意其他海报,更好的方法可能是将这些数据存储在XML,yaml中,或者可能存储在您从中获取的数据库中,并在那里访问它们。如果你确实需要在源文件中静态地创建它们,那么最好宣布一个对你的数据有意义的结构并初始化那些数组。类似的东西:
typedef struct
{
const char * somestring;
const char * someotherstring;
const unsigned int triple[3];
} Abbreviation;
const Abbreviation abb[] =
{
{"First Thing", "Second String", {1,2,3} },
{"Other Thing", "Some String", {4,5,6} }
};
答案 4 :(得分:1)
原始数据大约是1.7MB,来自其他2个文件,一个来自我的雇主,另一个来自Unicode Consortium(Unihan.txt,大约30MB)。使用字典查找技术,使用前128个最长和最频繁出现的单词的字典,仅将数据大小降低到1.5MB。我可以通过我的单词检测更加智能来改进它,这个目前只是空间上的VBScript Split()。
我没有任何关于准霍夫曼方法有多小的数字,但我的猜测是它略小于1MB。我想要将所有这些都放在二进制文件中,而不是作为一个单独的文件(尽管其他人可能会说有关不良实践的内容等等)。然而,就目前而言,它只是有点太难了,至少在C中。我可以弄清楚如何在幸福感中创建BSTR的变体数组......
编辑:我已经使用了关于标准UCN的字典查找,并且由于字形描述的重复性质,它运行良好。 Unihan的问题在于你最终描述了字形的含义; "VULGAR FRACTION ONE QUARTER"
和"A KIND OF PUNISHMENT IN HAN DYNASTY, NAME OF CHESSMEN IN CHINESE CHESS GAME(SIMPLIFIED FORM, A VARIANT U+7F75) TO CURSE; TO REVILE; TO ABUSE, TO SCOLD"
因此,从字典查找转向一些更强大的“压缩”技术。
(在有人说之前,“那么1.7MB的重要性是什么?”,我来自一个16K RAM很多的时代。而且我在任何情况下都有空间限制。)
答案 5 :(得分:0)
这个传奇似乎还没有结束。我最终把所有东西都变成了int
的不规则阵列。但随之而来的是三元组背后的自我指涉机制所依赖的项目的想法。
我现在正在考虑使用Euphoria而不是C,因为它对不规则数组的支持。可以用Euphoria构建标准的DLL,一旦我弄清楚如何交回BSTR的变体数组并写一个Typelib ......
请注意,我想我可以坚持使用C并将三元组连续存储为三个整数,并将这些字符串存储为整数的指针。这样可以省去我对VBScript的大量改写,它首先构建了自引用字典。