我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
有时候可以利用文件系统目录结构对数据进行索引,将数据存储为小文件,避免在大文件里搜索。文件系统的目录结构本身是个树结构,相当于索引树,但每个目录下的文件数不能太多,因为目录下文件是遍历查找的,同时也不能太少,因为每个目录节点本身也要占用空间,海量目录占用空间巨大。
本代码用字符串做key,若干个字节放一级目录(参数设置),如果设置为一个字节一级目录,那么key="123"的数据放置在“1/2/3/123.cache”文件里。
代码如下:
//快速文件索引,此结构本身可以放在共享内存
class CFastFileInex
{
private:
sstring<256> m_dir;//根目录
long section;//几个字节一层目录,同一个目录下文件超过一定数量打开文件会很慢
public:
char const * GetRoot()const { return m_dir.c_str(); }
string & toString(string & str)const
{
stringstream ss;
ss << "dir[" << m_dir.c_str() << "]" << section;
return str = ss.str();
}
void SetFastFileInex(char const * dir, long _section)
{
m_dir = dir;
section = _section;
}
string & ffiGetFileName(unsigned long key, string & filename)const
{
char buf[256];
sprintf(buf, "%lu", key);
return ffiGetFileName(buf, filename);
}
string & ffiGetFileName(char const * key, string & filename)const
{
filename = m_dir.c_str();
int len = strlen(key);
long k = len / section;
if (0 == len % section)--k;
for (long i = 0; i < k; ++i)
{
for (long j = 0; j < section; ++j) filename += key[i*section + j];
filename += '/';
}
filename += key;
filename += ".cache";
return filename;
}
bool ffiReadFile(unsigned long key, CBuffer & buffer)const
{
char buf[256];
sprintf(buf, "%lu", key);
return ffiReadFile(buf, buffer);
}
bool ffiReadFile(char const * key, CBuffer & buffer)const
{
string filename;
ffiGetFileName(key, filename);
//打开失败再创建目录,再次打开失败才算失败
CEasyFile file;
if (!file.ReadFile(filename.c_str(), buffer))
{
CDir::CreateDir(filename.c_str());
if (!file.ReadFile(filename.c_str(), buffer))
{
DEBUG_LOG << "打开文件出错 " << filename << " " << strerror(errno) << ende;
return false;
}
}
return true;
}
//读不到足够数据则失败
bool ffiReadFile(long key, char * buffer, long buffersize)const
{
char buf[256];
sprintf(buf, "%lu", key);
return ffiReadFile(buf, buffer, buffersize);
}
//读不到足够数据则失败
bool ffiReadFile(char const * key, char * buffer, long buffersize)const
{
string filename;
ffiGetFileName(key, filename);
//打开失败再创建目录,再次打开失败才算失败
CEasyFile file;
if (!file.ReadFile(filename.c_str(), buffer, buffersize))
{
CDir::CreateDir(filename.c_str());
if (!file.ReadFile(filename.c_str(), buffer, buffersize))
{
DEBUG_LOG << "打开文件出错 " << filename << " " << strerror(errno) << ende;
return false;
}
}
return true;
}
bool ffiWriteFile(long key, char const * buffer, long buffersize)const
{
char buf[256];
sprintf(buf, "%lu", key);
return ffiWriteFile(buf, buffer, buffersize);
}
bool ffiWriteFile(char const * key, char const * buffer, long buffersize)const
{
string filename;
ffiGetFileName(key, filename);
//打开失败再创建目录,再次打开失败才算失败
CEasyFile file;
if (!file.WriteFile(filename.c_str(), buffer, buffersize))
{
CDir::CreateDir(filename.c_str());
if (!file.WriteFile(filename.c_str(), buffer, buffersize))
{
DEBUG_LOG << "写入文件出错 " << filename << " " << strerror(errno) << ende;
return false;
}
}
return true;
}
bool ffiDeleteFile(long key)const
{
char buf[256];
sprintf(buf, "%lu", key);
return ffiDeleteFile(buf);
}
bool ffiDeleteFile(char const * key)const
{
string filename;
ffiGetFileName(key, filename);
//打开失败再创建目录,再次打开失败才算失败
CEasyFile file;
if (!file.DeleteFile(filename.c_str()))
{
DEBUG_LOG << "删除文件出错 " << filename << " " << strerror(errno) << ende;
return false;
}
return true;
}
};
里面用到的一些别的代码:
sstring 是个定长字符串模板类,替换成string就可以了
CDir 目录操作类,用到的函数的功能看名字就能理解了
CEasyFile 一个简单的文件接口类,用到的函数的功能看名字就能理解了
(这里是结束)