我想知道什么是最有效的内存阅读方式&在C中存储字符串列表。
每个字符串可能具有不同的长度,因此预先分配大型2D数组将是浪费。 我还想为每个字符串避免单独的malloc,因为可能有很多字符串。
字符串将从一个大缓冲区中读取到我要询问的列表数据结构中。
是否可以使用完全正确尺寸的单个分配来单独存储所有字符串?
我的一个想法是将它们连续存储在一个缓冲区中,然后有一个char *数组指向缓冲区中的不同部分,其中将包含其中的分隔符。我希望有更好的方法。
struct list {
char *index[32];
char buf[];
};
数据结构和字符串将严格为只读。
答案 0 :(得分:2)
这是一种温和有效的格式,假设您事先知道所有字符串的长度:
|| total size | string 1 | string 2 | ........ | string N | len(string N) | ... | len(string 2) | len(string 1) ||
您可以将长度存储为固定宽度整数或可变宽度整数,但重点是您可以跳到最后并相对有效地扫描所有长度,并从长度总和,您可以计算字符串的偏移量。当没有剩余空间时,您知道何时到达最后一个字符串。
答案 1 :(得分:0)
您可以创建单个缓冲区并连续存储它们,使用realloc()
根据需要扩展缓冲区。但是你需要第二个数组来存储字符串位置,也可能需要realloc()
,所以我可能只是分别创建一个动态分配的数组和malloc()
每个字符串。
答案 2 :(得分:0)
查找所有字符串的数量和总长度:
int num = 0;
int len = 0;
char* string = GetNextString(input);
while (string)
{
num += 1;
len += strlen(string);
string = GetNextString(input);
}
Rewind(input);
然后,分配以下两个缓冲区:
int* indexes = malloc(num*sizeof(int));
char* strings = malloc((num+len)*sizeof(char));
最后,填写以下两个缓冲区:
int index = 0;
for (int i=0; i<num; i++)
{
indexes[i] = index;
string = GetNextString(input);
strcpy(strings+index,string);
index += strlen(string)+1;
}
之后,您只需使用strings[indexes[i]]
即可访问第i个字符串。
答案 3 :(得分:0)
最有效和高效的内存方式是双通解决方案。在第一遍中,您计算所有字符串的总大小,然后分配总内存块。在第二遍中,您使用大缓冲区读取所有字符串。
您可以为字符串创建指针数组,并计算指针之间的差异以获取字符串大小。这样就可以将空字节保存为结束标记。
这是一个完整的例子:
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
struct StringMap
{
char *data;
char **ptr;
long cPos;
};
void initStringMap(StringMap *stringMap, long numberOfStrings, long totalCharacters)
{
stringMap->data = (char*)malloc(sizeof(char)*(totalCharacters+1));
stringMap->ptr = (char**)malloc(sizeof(char*)*(numberOfStrings+2));
memset(stringMap->ptr, 0, sizeof(char*)*(numberOfStrings+1));
stringMap->ptr[0] = stringMap->data;
stringMap->ptr[1] = stringMap->data;
stringMap->cPos = 0;
}
void extendString(StringMap *stringMap, char *str, size_t size)
{
memcpy(stringMap->ptr[stringMap->cPos+1], str, size);
stringMap->ptr[stringMap->cPos+1] += size;
}
void endString(StringMap *stringMap)
{
stringMap->cPos++;
stringMap->ptr[stringMap->cPos+1] = stringMap->ptr[stringMap->cPos];
}
long numberOfStringsInStringMap(StringMap *stringMap)
{
return stringMap->cPos;
}
size_t stringSizeInStringMap(StringMap *stringMap, long index)
{
return stringMap->ptr[index+1] - stringMap->ptr[index];
}
char* stringinStringMap(StringMap *stringMap, long index)
{
return stringMap->ptr[index];
}
void freeStringMap(StringMap *stringMap)
{
free(stringMap->data);
free(stringMap->ptr);
}
int main()
{
// The interesting values
long numberOfStrings = 0;
long totalCharacters = 0;
// Scan the input for required information
FILE *fd = fopen("/path/to/large/textfile.txt", "r");
int bufferSize = 4096;
char *readBuffer = (char*)malloc(sizeof(char)*bufferSize);
int currentStringLength = 0;
ssize_t readBytes;
while ((readBytes = fread(readBuffer, sizeof(char), bufferSize, fd))>0) {
for (int i = 0; i < readBytes; ++i) {
const char c = readBuffer[i];
if (c != '\n') {
++currentStringLength;
} else {
++numberOfStrings;
totalCharacters += currentStringLength;
currentStringLength = 0;
}
}
}
// Display the found results
printf("Found %ld strings with total of %ld bytes\n", numberOfStrings, totalCharacters);
// Allocate the memory for the resource
StringMap stringMap;
initStringMap(&stringMap, numberOfStrings, totalCharacters);
// read all strings
rewind(fd);
while ((readBytes = fread(readBuffer, sizeof(char), bufferSize, fd))>0) {
char *stringStart = readBuffer;
for (int i = 0; i < readBytes; ++i) {
const char c = readBuffer[i];
if (c == '\n') {
extendString(&stringMap, stringStart, &readBuffer[i]-stringStart);
endString(&stringMap);
stringStart = &readBuffer[i+1];
}
}
if (stringStart < &readBuffer[readBytes]) {
extendString(&stringMap, stringStart, &readBuffer[readBytes]-stringStart);
}
}
endString(&stringMap);
fclose(fd);
// Ok read the list
numberOfStrings = numberOfStringsInStringMap(&stringMap);
printf("Number of strings in map: %ld\n", numberOfStrings);
for (long i = 0; i < numberOfStrings; ++i) {
size_t stringSize = stringSizeInStringMap(&stringMap, i);
char *buffer = (char*)malloc(stringSize+1);
memcpy(buffer, stringinStringMap(&stringMap, i), stringSize);
buffer[stringSize-1] = '\0';
printf("string %05ld size=%8ld : %s\n", i, stringSize, buffer);
free(buffer);
}
// free the resource
freeStringMap(&stringMap);
}
此示例读取一个非常大的文本文件,将其拆分为行并创建一个每行一个字符串的数组。它只需要两次malloc
次呼叫。一个用于指针数组,一个用于sting块。
答案 4 :(得分:0)
如果它是如您所描述的那样严格只读,您可以将整个字符串及其偏移列表存储在单个内存块中,并通过一次读取来读取整个内容。
第一个sizeof(long) bytes
存储字符串数n
。下一个n
长号将偏移量存储在string buffer
开头的每个字符串中,该位置从位置(n + 1)* sizeof(long)开始。您不必为每个字符串存储尾随零,但如果这样做,您可以使用&amp; str_buffer [offset [i]]访问每个字符串。如果你不存储尾随&#39; \ 0&#39;然后你必须复制到一个临时缓冲区并自己附加。