存储数据的最有效方法是什么?


9

我负责重写一些旧的VB代码。我了解它是如何工作的,但是我觉得有一种更有效的方法来完成他们的工作。我只是不知道那是什么。这是一个人为的示例,它在数据需求方面确实与我需要做的相似。

用户必须在GUI中挑选汽车的制造商,制造商,型号和颜色。我有一个很大的文本文件,看起来像这样:

Ford Truck F150 red
Ford Truck F150 blue
Ford Truck F150 black
Ford Truck F150 silver
Ford Truck F250 red
Ford Truck F250 green
Ford Sedan Taurus red
Ford Sedan Taurus green
Ford Sedan Taurus white
Ford...
...

Subaru SUV Forester blue
Subaru SUV Forester red
Subaru SUV Outback Black
Subaru SUV Outback Green
Subaru SUV Outback Blue
Subaru SUV Outback Red
Subaru...
...

etc.

因此,如果第一个选择是斯巴鲁,第二个框(MAKE)应该不会有一个选项,选择卡车,因为没有斯巴鲁的是卡车。同样,如果他们选择的福特轿车和金牛座,那么最后盒(颜色)应该不会显示一个选项来选择蓝色。或黑色。或除红色,绿色或白色以外的任何其他颜色。

在我之前编写代码的人想到了这一点(在python-y psuedocode中):

def getValidOptions():
    items = []
    for i from 0 to numRows:
        options = getLine().split()
        if selectingManufacturer:
            if options[0] not in items:
                items.append(options[0])
        else if selectingMake:
            if selectedManufacturer == options[0] and options[1] not in items:
               items.append(options[1])
        else if selectingModel:
            if selectedManufacturer == options[0] and selectedMake == options[1] and options[2] not in items:
                items.append(options[2])
        else if selectingColor:
            if selectedManufacturer == options[0] and selectedMake == options[1] and selectedModel == options[2] and options[3] not in items:
                items.append(options[3])
    return items

我认为这在算法级别和语法级别都是令人毛骨悚然的。首先,它解析整个文件,如果正确完成的话,它仅需要读取几行。为了提高效率,我的真实数据有6个选项可供选择,而不仅仅是4个。给定数据重复量,这还存储了所需的更多数据。

我正在寻找将数据存储在文件中的其他方法,或者寻找使其getValidOptions功能更美观,更高效的解析方法。有什么办法可以做到这一点?


2
为什么不使用数据库?
图兰斯·科尔多瓦

Answers:


6

我阅读的所有其他答案似乎都忽略了软件开发的两个非常基本的规则:

  • 首先明确要求(尤其是性能和存储要求)

  • 保持简单,愚蠢(请参阅KISS

您写了“文本文件很大”,但没有写太大,所以我认为除了您的直觉外,它的大小实际上没有任何问题。因此,如果加载文件实际上并不会花费太长时间,并且您的IT部门或其他人没有抱怨磁盘空间浪费,并且没有人抱怨维护文件有问题,请保持文件格式不变-不要小看简单的价值。

您还抱怨算法的效率,虽然实际上效率不高,但是它有一个很大的优势:脑死亡很简单并且可以工作。因此,只要它足够有效,就不要应用任何过早的优化。

因此,假设程序运行速度足够快,您首先要问的是,如何在简单性和DRY原理方面改进代码?这确实是一个应该改进的地方,因为当前代码不是DRY。在您的示例中,如果“较高级别”上的选项与当前行匹配,则会出现几乎四次几乎相同的代码块测试,这导致四次相同类型的“追加”调用(在您的真实代码中,重复发生至少写了六遍)。您可以通过引入数字选择级别或在顺序列表中传递已经选择的选项来轻松避免这种情况。使用您的伪代码,这会导致类似

# selectedOptions is a list, containing either nothing, or "selectedManufacturer"
# or [selectedManufacturer, selectedMake], ..., and so on
def getValidOptions(selectedOptions):
    items = []
    level = selectedOptions.size()
    for i from 0 to numRows:
        options = getLine().split()
        if selectedOptions == options[0:level-1] and options[level] not in item:
            items.append(options[level])
    return items

因此,这基本上是相同的算法,不再有重复的代码。

由于很明显getValidOptions必须调用一次以上(每级至少调用一次),因此我建议仅应用一种优化(如果不是这种情况):确保该getLine函数从主内存中提取其数据,并且不要一次又一次地从磁盘读取文件。


您想将“ level = selectedOptions.size()”移动到numRows循环之前。
AI Breveleri 2015年

6

嗯,您拥有的数据具有树状结构,其中每个制造商都有一棵模型树,而每种模型都有一种颜色(依此类推)。

因此,您可以分两步来分离此数据的过程:

  1. 对文本文件进行任何更新之后,您必须处理该文件文件并将其转换为树形结构。
  2. 加载应用程序时,仅加载树结构。

可以使用Java,PHP,Javascript或Python之类的哈希关联数组字典来实现树结构。通过这种结构,您将:

  • 第一钥匙是制造商。
  • 它们的值只是另一个哈希字典,其中每个键都是模型。
  • 它们的价值就是颜色。或另一种结构,在它们的键中保留第三级,并保留第四级的价值。
  • 等等...

根据您的编程语言,可以更快或更慢地实现。例如:

  • C#:您可以实现Tree结构1,然后将其标记为可序列化的。
  • VB.Net:您可以在一个应用程序中生成树结构,并将其序列化为文件。
    • 为此,类似的东西Runtime.Serialization.Formatters.Binary.BinaryFormatter可能会有用,但是我不是VB.Net序列化方面的专家。
  • Javascript:您可以在JSON文件中生成树结构,每次加载应用程序时都必须加载该树结构。
  • PHP:您可以生成树数据结构的序列化版本,也可以加载JSON。
  • Java:您可以序列化该数据结构,创建一个Class实现接口的java.io.Serializable

参考文献

1:https//dvanderboom.wordpress.com/2008/03/15/treet-implementing-a-non-binary-tree-in-c/-
有关在C#中实现树的完整说明。
-在某人询问有关序列化该对象的地方查找注释,以及该注释的答案。


1
是的,我曾经考虑过使用一棵树,但是我不知道这是否是最好的主意,因为在C#中没有内置的树结构(我知道),而且项目很小,所以我不知道花很多时间在tree with an arbitrary number of nodes实现上是值得的。
詹姆斯

到目前为止,我还不是C#的专家,但是至少在其他语言(如Java和PHP)中,您可以拥有某种字典,其中每个键都可以具有另一个字典的价值。
尼古拉斯

我更新了答案。查看您对哈希或字典替代方案的看法。我还添加了一篇有趣的文章作为参考。
尼古拉斯

3

一种简单的存储数据的方法是将其填充到SQLite数据库中。与大多数SQL数据库不同,SQLite非常适合用作应用程序文件格式。这种方法有几个好处:

  1. 无需编写序列化器或反序列化器。
  2. 文件可以通过许多现有程序进行编辑和查询。
  3. 您避免在问题中提到的有条件的混乱。要限制下拉列表,请针对每列(例如select distinct model where manufacturer='ford' and color = 'red')生成一个简单的where子句。

这确实迫使您学习SQL,但是避免了学习自定义文件格式的需要。


1

我假设您可以随机访问文件中的行,因此可以将文件视为记录数组。如果您不能随机访问这些行,那么您拥有的算法就是您可以做到的最好的算法。

为了最快地访问,请将数据存储在6个文件中,其中每个文件是下一个文件的索引。

有许多创建平面文件索引的方法。我通常使用下标范围。当用户进行每个选择时,请使用范围限制下一个文件的读取。

这是我如何为您提供的样本数据创建索引。

当然必须对文件进行排序。我已经编号了线以便说明;行号不应出现在文件中。

--| file3.dat |--
 0 Ford Truck F150 red
 1 Ford Truck F150 blue
 2 Ford Truck F150 black
 3 Ford Truck F150 silver
 4 Ford Truck F250 red
 5 Ford Truck F250 green
 6 Ford Sedan Taurus red
 7 Ford Sedan Taurus green
 8 Ford Sedan Taurus white
 9 Subaru SUV Forester blue
10 Subaru SUV Forester red
11 Subaru SUV Outback Black
12 Subaru SUV Outback Green
13 Subaru SUV Outback Blue
14 Subaru SUV Outback Red

要创建第一个索引,请为文件中前三个字段的每个唯一组合进行记录。在每个记录中,存储出现该组合的第一行和最后一行。

--| file2.dat |--
 0 Ford Truck F150       0   3
 1 Ford Truck F250       4   5
 2 Ford Sedan Taurus     6   8
 3 Subaru SUV Forester   9  10
 4 Subaru SUV Outback   11  14

使用第一个索引的前两个字段以类似方式构造第二个索引。

--| file1.dat |--
 0 Ford Truck        0   1
 1 Ford Sedan        2   2
 2 Subaru SUV        3   4

第三,在这种情况下,顶层是索引。

--| file0.dat |--
 0 Ford          0   1
 1 Subaru        2   2

我认为我对此概念进行了过多的解释,但总的来说,您可以通过删除最后一个字段并消除重复的记录来创建索引。

您可以通过消除一些冗余数据来进一步减少文件存储需求。

例如,每个索引记录中的“第一个”下标总是比先前记录的“最后一个”下标多一个,如果没有先前的记录,则为零。因此,您不需要存储“第一个”下标。我将它们留在下面以进行说明。

另外,由于您将仅使用每个记录中的最后一个字段来填充选择列表,因此您无需存储其他字段。

索引级联的最小再现最终看起来像这样,其中的勾号'表示未实际存储在文件中的数字。

--| file0.dat |--
 0' Ford         0'   1
 1' Subaru       2'   2

--| file1.dat |--
 0' Truck        0'   1
 1' Sedan        2'   2
 2' SUV          3'   4

--| file2.dat |--
 0' F150         0'   3
 1' F250         4'   5
 2' Taurus       6'   8
 3' Forester     9'  10
 4' Outback     11'  14

--| file3.dat |--
 0' red
 1' blue
 2' black
 3' silver
 4' red
 5' green
 6' red
 7' green
 8' white
 9' blue
10' red
11' Black
12' Green
13' Blue
14' Red

当您从索引中填充选择列表时,将使用上一个索引中用户选择的“第一”和“最后”下标来限制读取的行。

例:

您从中全部填写第一个选择列表file0.dat。(福特,斯巴鲁)

用户选择“福特”。对应的下标为0和1。

您从的第0行到第1行填充第二个选择列表file1.dat。(轿车,轿车)

用户选择“轿车”。对应的下标是2和2。

如您所见,当用户选择“ Ford”,“ Sedan”,“ Taurus”时,您会发现只需要读取第6行到第8行file3.dat即可填充第四个选择列表。

对于冗长的描述,我深表歉意,但现在已经很晚了,我没有时间写一个简短的描述。

添加:经过进一步考虑,可以将文件串联在一起。

--| file.dat |--
 0' -            1'   2
 1' Ford         3'   4
 2' Subaru       5'   5
 3' Truck        6'   7
 4' Sedan        8'   8
 5' SUV          9'  10
 6' F150        11'  14
 7' F250        15'  16
 8' Taurus      17'  19
 9' Forester    20'  21
10' Outback     22'  25
11' red          -'   -
12' blue         -'   -
13' black        -'   -
14' silver       -'   -
15' red          -'   -
16' green        -'   -
17' red          -'   -
18' green        -'   -
19' white        -'   -
20' blue         -'   -
21' red          -'   -
22' Black        -'   -
23' Green        -'   -
24' Blue         -'   -
25' Red          -'   -

这与多文件版本完全一样,只是您需要虚拟的第一行来包含第一个下标范围。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.