Algorithm 为csv文件中的列编制索引

Algorithm 为csv文件中的列编制索引,algorithm,performance,csv,search,indexing,Algorithm,Performance,Csv,Search,Indexing,我有一个大的csv文件,每行有不同的列,如ID、用户名、电子邮件、职位等 我想通过精确匹配(用户名==David)或通配符(jobPosition==admin)搜索行 我想索引此文件中的列以加快搜索速度,但我不知道应该选择哪种算法(特别是对于通配符)。短版本。将CSV加载到SQLite中,然后查询它。您可以在上了解SQLite,但我建议您在您的语言中寻找一个已经拥有它的库 长版本 在您弄清楚如何编写代码之前,您可以将数据加载到SQLite中,对其进行索引、查询,然后就可以完成了。如果您目前不知

我有一个大的csv文件,每行有不同的列,如ID、用户名、电子邮件、职位等

我想通过精确匹配(用户名==David)或通配符(jobPosition==admin)搜索行


我想索引此文件中的列以加快搜索速度,但我不知道应该选择哪种算法(特别是对于通配符)。

短版本。将CSV加载到SQLite中,然后查询它。您可以在上了解SQLite,但我建议您在您的语言中寻找一个已经拥有它的库

长版本

在您弄清楚如何编写代码之前,您可以将数据加载到SQLite中,对其进行索引、查询,然后就可以完成了。如果您目前不知道如何编写SQL,这一点甚至是正确的。(相信我,我知道您需要的算法,学习它们比学习SQL更难。)

在您真正完成代码编写之前,您的另一个助手将完成其他几个项目

编写代码之后,就可以进行调试了。我保证你不会成功调试它。与此同时,在另一个宇宙中,你继续建设更多的项目

一旦您调试了代码并将其投入生产(未知bug仍然存在),您就可以跳过初始加载步骤。同时,您的另一个宇宙本身甚至不必考虑这样一个事实:SQLite是用非常高效的C语言实现的,使用的优化器可能与“真实”数据库不匹配,但比您自己可以使用的任何优化器都要好

鉴于此,您确实应该考虑使用SQLite .< /P> PS:解释如何在SQLite中进行通配符匹配。

如何为csv文件编制索引: 要为csv文件编制索引,您需要将其作为二进制文件而不是文本文件读取。使用128、256或512块大小。要构建索引,请扫描文件以查找每个记录的开头,然后创建如下索引文件:

  key-value-1, 0, 0
   ........
   ........
  key-value-n, block, offset
key value是您正在索引的键的值。可以是复合密钥block是记录开始的块编号(请注意,您的记录可以跨越多个块),offset是介于0block-size-1之间的数字,即进入该块的偏移量。要检索记录,请在索引文件上查找键(使用maybe),然后使用块偏移量访问记录

如果需要搜索不同的条件,也可以同时创建多个索引文件

如果您将
CR-LF
作为行尾标记,请注意
CR
可以位于块的精确末端,而
LF
将位于下一个块的最开始。你需要注意一些像这样的特殊情况。一旦您创建了这个索引文件(或多个文件),您就可以按键对其进行排序,这样就可以开始了

可选地,如果您的软件允许快速内存块移动(如C++ <强> <强> >),可以使用<强>与二进制搜索>。这样,在您完成索引构建之后,它们就已经被排序了。如果索引项是从使用慢速输入设备(例如,键盘)捕获的文件中添加的,则这种方法特别有效。如果您管理大量的记录,请考虑使用索引(ES)的结构。

此模式允许csv数据库接受记录添加删除更新在文件末尾添加。若要删除记录,只需将记录的第一个字符更改为唯一字符,如
0x0
,当然,还可以从索引文件中删除该条目更新可以通过删除并在文件末尾添加更新的记录来实现

这将在您的数据库上创建一些需要,但大多数(如果不是全部的话)都需要这样做。定期重建索引并清除已删除的记录

注意:为一个包含6867839行的9 Gb csv文件编制索引的代码实现大约需要6分钟。Joblib在磁盘上存储索引的速度非常慢。索引文件为134MB

让我们使用一个玩具csv示例。我们将按记录编号为该文件编制索引。为了便于示例,我们将在索引的关键部分存储记录编号,尽管这显然是不必要的

strings,numbers,colors
string1,1,blue
string2,2,red
string3,3,green
string4,4,yellow
索引文件将存储在列表idx中:

 idx

 [[0, 0, 0], [1, 0, 24], [2, 0, 40], [3, 0, 55], [4, 0, 72], [5, 0, -1]]
def get_rec(n=1,binary=False):
    n=1 if n<0 else n+1
    s=b'' if binary else '' 
    if len(idx)==0:return ''
    if idx[n-1][2]==-1:return ''
    f.seek(idx[n-1][1]*BLKSIZE+idx[n-1][2])
    buff=f.read(BLKSIZE)
    x=buff.find(b'\r')
    while x==-1:
        s=s+buff if binary else s+buff.decode()
        buff=f.read(BLKSIZE)
        x=buff.find(b'\r')
    return s+buff[:x]+b'\r\n' if binary else s+buff[:x].decode()
注意,最后一个索引元素处的-1表示顺序访问时索引文件的结尾。您可以使用这样的代码按记录编号访问csv文件的任何一行:

 idx

 [[0, 0, 0], [1, 0, 24], [2, 0, 40], [3, 0, 55], [4, 0, 72], [5, 0, -1]]
def get_rec(n=1,binary=False):
    n=1 if n<0 else n+1
    s=b'' if binary else '' 
    if len(idx)==0:return ''
    if idx[n-1][2]==-1:return ''
    f.seek(idx[n-1][1]*BLKSIZE+idx[n-1][2])
    buff=f.read(BLKSIZE)
    x=buff.find(b'\r')
    while x==-1:
        s=s+buff if binary else s+buff.decode()
        buff=f.read(BLKSIZE)
        x=buff.find(b'\r')
    return s+buff[:x]+b'\r\n' if binary else s+buff[:x].decode()
def get_rec(n=1,binary=False):

n=1如果您没有访问数据库系统的权限,您可以将其导入?@MartinBroadhurst,这是一个自我培训项目,我只想了解通配符索引算法,我在btilly的回答下写了2条注释来解释它。总的来说,我同意并喜欢SQLite,但具体地说,我在过去做过这项工作,发现在某些情况下,导入可能需要几十分钟——在现代SSD硬件上,CSV输入的最高速度约为每秒30次插入。因此,这是一个很好的起点(下次我需要那个项目时,我会尽量加快速度),但并不总是一个可行的选择。@eftpotrm我发现“30次插入/秒”的速度低得惊人。我会立即怀疑问题是触发器、索引和外键的某种组合。你是不是做了标准的加载数据,然后创建了索引,而不是试图加载有索引的数据?我同意,这让我很惊讶!非常小的数据库,当然没有触发器-不能说话的键和索引关闭我的头顶。下次我需要优化时,我会调查它,因为我当然喜欢SQLite作为解决方案(并且认为它可能是OP的正确解决方案),但是我