`
zhc0822
  • 浏览: 228813 次
  • 性别: Icon_minigender_1
  • 来自: 宝仔的奇幻城堡
社区版块
存档分类
最新评论

内存受限下的设计模式(5)——数据文件

阅读更多
背景
数据量太大,主存容纳不下怎么办?
有时候,尽管程序本身很小巧,但却得处理大量数据(比如BT客户端)。这种情况意味着,尽管程序本身可以进入主存,但是数据却耗费了太多内存。

模式
因此,我们应当一次只处理一部分数据;其余放在辅助存储设备中内。

更深入的探讨
我们可以利用循序访问或随机访问读取IO流中的每一笔数据并进行处理,再把处理完毕的数据循序写回一个或多个文件。
这个模式并不是很复杂,适用的场景也非常广泛,但是我们应当清楚该模式的局限。
首先分批处理数据会增加实现的局部复杂性。
其次,需要额外的环境信息以便处理数据(比如链接程序)。
再次,读取多个小型数据项往往比读取一份大型数据项效率要低。
最后,如果对数据有一些要求(比如要求从文件读取10000个数值,在输入之前应当先对这10000个数值排序),那么使用该模式则会增加系统的复杂度,降低可用性。

实现
实现该模式的方法主要有三种:
  • 增量处理;
  • 子文件处理;
  • 随机访问;

1.增量处理
这是最简单也是最常见的一个方法,即从IO流循序读入整个文件,并将处理结果循序写入另一个文件。一般来说,这种方法适用于输入也是按序的场景:比如HTTP下载、读取用户输入等等。
2.子文件处理
如果不以循序方式处理文件,也可以将数据划分为多个小型文件。此时,还需要单独编写一个程序用来处理生成的每一个小文件然后将这些小文件合并为一个大文件。
然而,数据的划分并不是那么容易,大多数情况下,需要额外的信息来记录划分的边界。
3.随机访问
当然,你也可以随机访问某一个文件。比如,在UNIX下,你可以使用lseek()函数在文件中定位。使用这种方法也需要额外的环境信息用以记录一些信息,比如UNIX中的文件描述符。

示例
1.增量处理
以下是QT的一个例子(只截取了部分代码)
void HttpWindow::startRequest(QUrl url)
{
    reply = qnam.get(QNetworkRequest(url));
    connect(reply, SIGNAL(finished()),
            this, SLOT(httpFinished()));
    // 连接信号与槽,即每当有新的输入时就发射readyRead信号,触发httpReadyRead槽
    connect(reply, SIGNAL(readyRead()),
            this, SLOT(httpReadyRead()));
    connect(reply, SIGNAL(downloadProgress(qint64,qint64)),
            this, SLOT(updateDataReadProgress(qint64,qint64)));
}

然后来看我们是如何实现私有槽httpReadyRead的:
void HttpWindow::httpReadyRead()
{
    // 每次有新数据就写入一次,这样可以减少对RAM的需求。
    if (file)
        file->write(reply->readAll());
}


2.子文件处理
编译器是子文件处理的一个典型应用。用户把大型程序分解为多个文件,编译器逐一处理,然后链接器将所有的.o文件合并为一个可执行程序:
gcc -c foo1.c -o foo1.o
gcc -c foo2.c -o foo2.o
gcc foo1.o foo2.o -o foo


3.随机访问
参考UNIX文件IO的一些例子即可。不再赘述。

附件
附件中是使用QT实现的一个简单的下载程序。我在示例中已经提取了关键部分讲解。

预告
下一篇,介绍资源文件,一个应用十分广泛的模式。
1
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics