C语言从一个文件中读取乱序内容,并排序,数据量比较大怎么弄?谢谢

一升米 2017-11-22 03:26:28
C语言程序,A文件中有几十万条数据,每条数据有一个id不重复,id目前是乱序,如何将A中的数据排序后输出到B文件中,目前做法是读取A文件中的每条数据,缓存,再取新数据,和缓存的比较id,将新数据插入缓存,然后所有的数据取完,一次性输出到B中,但是这样太慢了,数据越多,缓存的越慢,比较的也越慢,有什么别的好方法么?谢谢,感觉目前的排序算法太坑了,效率越来越慢,A文件中的数据是变化的,可能几千条的概率是60%,几万条的概率是20%,几十万条的概率是13%,几百万条是7%
...全文
1410 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
大尾巴猫 2017-11-23
  • 打赏
  • 举报
回复
1、内存足够,读一次文件,所有数据排序,再写新文件,是最快的,比逐个读记录边读边插入肯定快。 2、我说的先提取排序关键字和偏移,排序后再读取再写入,是2次文件读取1次写,是慢一点,但是可以对付内存不够放下整个文件的情况。 3、至于排序的关键字不是记录的首字节,并不是很麻烦的事情,偏移是该条记录的首字节对应在文件中的位置,是需要解析记录再获取的。如果记录不等长,还需要一个字段记录该条记录的长度。 4、操作系统读文件,如果读某个偏移开始的位置,应该不是从头开始读磁盘的,可以根据文件分配表的内容,直接找到该偏移对应的物理扇区。不是偏移量越大,每次读取的时间越长。
xg436 2017-11-23
  • 打赏
  • 举报
回复
这样第一条记录就是id为1的记录了
xg436 2017-11-23
  • 打赏
  • 举报
回复
引用 25 楼 sys0613 的回复:
[quote=引用 23 楼 xg436 的回复:] 又看了一遍,你是可以直接缓存A中所有数据?那就新建个数组,和字符串数组,第一条的id是100,那就int【100】=1,string【0】=内容,然后写b就string【int【i++】】
完全没看懂,缓存了不是还要排序么?[/quote] 不需要了 比如A里的ID依次为35214,存储到str[]里面,那么你按我说的int数组里int[1,2,3,4,5]对应的是[4,3,1,5,2],然后str[int[1]]=str[4]=1
赵4老师 2017-11-23
  • 打赏
  • 举报
回复
命令提示实用工具 bcp 实用工具 bcp 实用工具在 Microsoft® SQL Server™ 2000 实例和数据文件之间以用户指定的格式复制数据。 语法 bcp {[[database_name.][owner].]{table_name | view_name} | "query"} {in | out | queryout | format} data_file [-m max_errors] [-f format_file] [-e err_file] [-F first_row] [-L last_row] [-b batch_size] [-n] [-c] [-w] [-N] [-V (60 | 65 | 70)] [-6] [-q] [-C code_page] [-t field_term] [-r row_term] [-i input_file] [-o output_file] [-a packet_size] [-S server_name[\instance_name]] [-U login_id] [-P password] [-T] [-v] [-R] [-k] [-E] [-h "hint [,...n]"] 参数database_name 指定的表或视图所在数据库的名称。如果未指定,则为用户默认数据库。 owner 表或视图所有者的名称。如果执行大容量复制操作的用户拥有指定的表或视图,则 owner 是可选的。如果没有指定 owner 并且执行大容量复制操作的用户不拥有指定的表或视图,则 Microsoft® SQL Server™ 2000 将返回错误信息并取消大容量复制操作。 table_name 是将数据复制到 SQL Server 时 (in) 的目的表名,以及从 SQL Server 复制数据时 (out) 的源表名。 view_name 是将数据复制到 SQL Server 时 (in) 的目的视图名,以及从 SQL Server 复制数据时 (out) 的源视图名。只有其中所有列都引用同一个表的视图才能用作目的视图。有关将数据复制到视图的限制的更多信息,请参见 INSERT。 Query 是返回一个结果集的 Transact-SQL 查询。如果查询返回多个结果集,例如指定 COMPUTE 子句的 SELECT 语句,只有第一个结果集将复制到数据文件,随后的结果集被忽略。使用双引号引起查询语句,使用单引号引起查询语句中嵌入的任何内容。在从查询中大容量复制数据时,还必须指定 queryout。 in | out | queryout | format 指定大容量复制的方向。in 是从文件复制到数据库表或视图,out 是指从数据库表或视图复制到文件。只有从查询中大容量复制数据时,才必须指定 queryout。根据指定的选项(-n、-c、-w、-6 或 -N)以及表或视图分隔符,format 将创建一个格式文件。如果使用 format,则还必须指定 -f 选项。 说明 Microsoft SQL Server 6.5 中的 bcp 实用工具不支持大容量复制到包含 sql_variant 或 bigint 数据类型的表。 data_file 大容量复制表或视图到磁盘(或者从磁盘复制)时所用数据文件的完整路径。当将数据大容量复制到 SQL Server 时,此数据文件包含将复制到指定表或视图的数据。当从 SQL Server 大容量复制数据时,该数据文件包含从表或视图复制的数据。路径可以有 1 到 255 个字符。 -m max_errors 指定在大容量复制操作取消之前可能产生的错误的最大数目。bcp 无法复制的每一行都将被忽略并计为一个错误。如果没有包括该选项,则默认为 10。 -f format_file 指定格式文件的完整路径,该格式文件包含以前在同一个表或视图上使用 bcp 时的存储响应。当使用由 format 选项所创建的格式文件大容量复制入或复制出数据时,使用此选项。格式文件的创建是可选的。在提示几个格式问题之后,bcp 将提示是否在格式文件中保存回答。默认文件名为 Bcp.fmt。大容量复制数据时,bcp 可引用一个格式文件,因此不必重新交互输入以前的回答。如果未使用此选项,也没有指定 –n、-c、-w、-6 或 -N,则 bcp 将提示输入格式信息。 -e err_file 指定错误文件的完整路径,此错误文件用于存储 bcp 无法从文件传输到数据库的所有行。来自 bcp 的错误信息将发送到用户工作站。如果未使用此选项,则不创建错误文件。 -F first_row 指定要大容量复制的第一行的序数。默认值是 1,表示在指定数据文件的第一行。 -L last_row 指定要大容量复制的最后一行的序数。默认值是 0,表示指定数据文件中的最后一行。 -b batch_size 指定所复制的每批数据中的行数。每个批处理作为一个事务复制至服务器。SQL Server 提交或回滚(在失败时)每个批处理的事务。默认情况下,指定的数据文件中的所有数据都作为一批复制。请不要与 -h "ROWS_PER_BATCH = bb" 选项一起使用。 -n 使用数据的本机(数据库)数据类型执行大容量复制操作。此选项不提示输入每一字段,它将使用本机值。 -c 使用字符数据类型执行大容量复制操作。此选项不提示输入每一字段;它使用 char 作为存储类型,不带前缀,\t(制表符)作为字段分隔符,\n(换行符)作为行终止符。 -w 使用 Unicode 字符执行大容量复制操作。此选项不提示输入每一字段;它使用 nchar 作为存储类型,不带前缀,\t(制表符)作为字段分隔符,\n(换行符)作为行终止符。不能在 SQL Server 6.5 版或更早版本中使用。 -N 对非字符数据使用数据的本机(数据库)数据类型和对字符数据使用 Unicode 字符类型执行大容量复制操作。这是可替代 -w 选项的性能更高的选项,其目的是使用数据文件将数据从一个 SQL Server 传输到另一个 SQL Server 中。它不提示输入每一字段。在需要传输包含 ANSI 扩展字符的数据以及想利用本机模式的性能时,可以使用这一选项。不能在 SQL Server 6.5 版或更早版本中使用 -N 选项。 -V (60 | 65 | 70) 使用 SQL Server 早期版本中的数据类型执行大容量复制操作。此选项与字符 (-c) 或本机 (-n) 格式一起使用。此选项并不提示输入每一字段,它使用默认值。例如,若要将 SQL Server 6.5 中的 bcp 实用工具所支持(但 ODBC 不再支持)的日期格式大容量复制到 SQL Server 2000,可使用 -V 65 参数。 重要 将数据从 SQL Server 大容量复制到数据文件时,即使指定了 –V,bcp 实用工具也不会为任何 datetime 或 smalldatetime 数据生成 SQL Server 6.0 或 SQL Server 6.5 的日期格式。日期将始终以 ODBC 格式写入。另外,由于 SQL Server 6.5 版或更早版本不支持可为空的 bit 数据,因此 bit 列中的空值写为值 0。 -6 使用 SQL Server 6.0 或 SQL Server 6.5 数据类型执行大容量复制操作。仅为保持向后兼容性。改为使用 –V 选项。 -q 在 bcp 实用工具和 SQL Server 实例的连接中执行 SET QUOTED_IDENTIFIERS ON 语句。使用该选项指定包含空格或引号的数据库、所有者、表或视图的名称。将由三部分组成的整个表名或视图名引在双引号 (" ") 中。 -C code_page 仅为保持向后兼容性。作为代替,请在格式文件或交互式 bcp 中为每一列指定一个排序规则名。 指定数据文件中的数据代码页。只有当数据中包含字符值大于 127 或小于 32 的 char、varchar 或 text 列时,code_page 才有用。 代码页值 描述 ACP ANSI/Microsoft Windows® (ISO 1252)。 OEM 客户程序使用的默认代码页。如果未指定 -C,则这是 bcp 使用的默认代码页。 RAW 不发生从一个代码页到另一个代码页的转换。因为不发生转换,所以这是最快的选项。 <值> 特定的代码页号码,例如 850。 -t field_term 指定字段终止符。默认的字段终止符是 \t(制表符)。使用此参数替代默认字段终止符。 -r row_term 指定行终止符。默认的行终止符是 \n(换行符)。使用此参数替代默认行终止符。 -i input_file 指定响应文件的名称,使用交互模式(未指定 –n、-c、-w、-6 或 -N)执行大容量复制时,响应文件包含对每一字段命令提示问题的响应。 -o output_file 指定接收 bcp 输出(从命令提示重定向)的文件的名称。 -a packet_size 指定发送到和发送自服务器的每个网络数据包的字节数。可以使用 SQL Server 企业管理器(或 sp_configure 系统存储过程)设置服务器配置选项。但是,使用此选项可以单个地替代服务器配置选项。packet_size 可以设置为 4096 到 65535 字节,默认值为 4096。 数据包大小的增加能够提高大容量复制操作的性能。如果要求一个较大的数据包而得不到,则使用默认设置。bcp 生成的性能统计显示出所使用数据包的大小。 -S server_name[\instance_name] 指定要连接到的 SQL Server 实例。指定 server_name 以连接该服务器上的 SQL Server 默认实例。指定 server_name\instance_name 以连接到该服务器上的 SQL Server 2000 命名实例。如果未指定服务器,则 bcp 连接到本地计算机上的 SQL Server 默认实例。从网络上的远程计算机执行 bcp 时,要求此选项。 -U login_id 指定用于连接到 SQL Server 的登录 ID。 -P password 指定登录 ID 的密码。如果未使用此选项,则 bcp 将提示输入密码。如果不带密码将此选项用于命令提示行末尾,则 bcp 将使用默认密码 (NULL)。 -T 指定 bcp 使用网络用户的安全凭据,通过信任连接连接到 SQL Server。不需要 login_id 和 password。 -v 报告 bcp 实用工具的版本号和版权。 -R 指定使用为客户端计算机的区域设置定义的区域格式,将货币、日期和时间数据大容量复制到 SQL Server 中。默认情况下,将会忽略区域设置。 -k 指定在大容量复制操作中空列应保留一个空值,而不是对插入的列赋予默认值。 -E 指定标识列的值出现在要导入的文件中。如果没有给出 -E,则正导入的数据文件中此列的标识值将被忽略,而且 SQL Server 2000 会根据创建表期间指定的种子值和增量值自动指派唯一的值。如果数据文件的表或视图中不包含标识列的值,则使用格式文件指定导入数据时应跳过表或视图中的标识列;SQL Server 2000 将自动为该列指派唯一值。有关详细信息,请参见 DBCC CHECKIDENT。 -h "hint [,...n]" 指定在大容量复制数据到表或视图时所使用的提示。在大容量复制数据到 SQL Server 6.x 或更早版本时,不能使用此选项。 提示 描述 ORDER (column [ASC | DESC] [,...n]) 数据文件中数据的排序次序。如果要装载的数据已根据表中的聚集索引排序,则会提高大容量复制的性能。如果数据文件按不同次序排序,或者该表没有聚集索引,则将忽略 ORDER 提示。所提供的列名必须是目的表中的有效列。默认情况下,bcp 假设数据文件没有排序。 ROWS_PER_BATCH = bb 每批中数据的行数(即 bb)。在未指定 -b 时使用,这将使整个数据文件作为单个事务发送到服务器。服务器根据值 bb 优化大容量装载。默认情况下,ROWS_PER_BATCH 未知。 KILOBYTES_PER_BATCH = cc 每批中数据的千字节 (KB) 近似数量(即 cc)。默认情况下,KILOBYTES_PER_BATCH 未知。 TABLOCK 大容量复制操作期间将获取表级锁。由于只在大容量复制操作期间才控制锁减少了表中锁的争夺,因此此提示可以显著提高性能。如果表没有索引并且指定了 TABLOCK,则该表可以同时由多个客户端装载。默认情况下,锁定行为是由表选项 table lock on bulk load 决定的。 CHECK_CONSTRAINTS 大容量复制操作期间,将检查目的表上的所有约束。默认情况下,将会忽略约束。 FIRE_TRIGGERS 与 in 参数一起指定,在目的表上定义的任何插入触发器将在大容量复制操作期间执行。如果没有指定 FIRE_TRIGGERS,则不执行插入触发器。对于 out、queryout 和 format 参数,将忽略 FIRE_TRIGGERS。 注释 将忽略要导入的数据文件中计算列或 timestamp 列的值,SQL Server 2000 自动赋值。如果数据文件不包含表中的计算列或 timestamp 列的值,可用格式文件指定应在导入数据时跳过表中的计算列和 timestamp 列;SQL Server 将自动为该列赋值。 计算列和 timestamp 列照常会从 SQL Server 大容量复制到一个数据文件。 SQL Server 标识符(包括数据库名称、表名或视图名、登录和密码)可以包含诸如嵌入空格和引号等字符。当在命令提示符处指定包含空格或引号的标识符或文件名时,需要将该标识符引在双引号 (" ") 内。另外,对于包含嵌入空格或引号的所有者、表或视图的名称,可以指定 -q 选项,或者将所有者、表或视图的名称在双引号内用方括号 ([ ]) 括起来。 例如,Northwind 数据库内有表 Jane's Orders,该表由用户 Jane Doe 所拥有。若要使用登录 Jane Doe 和密码 go dba 将该表从 Northwind 数据库大容量复制到 Orders.txt 文件,请执行下列命令之一: bcp "Northwind.Jane Doe.Jane's Orders" out "Jane's Orders.txt" -c -q -U"Jane Doe" -P"go dba" bcp "Northwind.[Jane Doe].[Jane's Orders]" out "Jane's Orders.txt" -c -U"Jane Doe" -P"go dba" 若要指定包含空格或引号的数据库名称,必须使用 -q 选项。 有关此实用工具位置和运行方式的信息,请参见命令提示实用工具入门。 请参见 在不同排序规则间复制数据 使用 bcp 和 BULK INSERT 并行数据装载 SET QUOTED_IDENTIFIER sp_tableoption 使用格式文件 ©1988-2000 Microsoft Corporation。保留所有权利。 管理 SQL Server 指定数据格式 如果正在 Microsoft® SQL Server™ 实例和其它程序(例如另一个数据库程序)之间复制数据,则默认的数据类型格式(本机、字符或 Unicode)可能与其它程序所预期的数据结构不兼容。因此,bcp 实用工具允许指定有关数据文件结构的更详细的信息。 如果没有指定 -n、-c、-w 或 -N 开关,则 bcp 实用工具会交互式地提示输入正在复制的每个列的进一步信息: 文件存储类型 前缀长度 字段长度 字段终止符 说明 在使用 BULK INSERT 语句时不能使用交互模式。 根据源列或目的列的 SQL Server 数据类型,bcp 实用工具在每个提示中都会提供默认值。接受 bcp 在这些提示中提供的默认值将生成与本机格式 (-n) 相同的结果,并提供一种方法,以将数据从其它程序中大容量复制出来,以便日后重新装载到 SQL Server 中。 可以创建一个格式文件,用来存储对数据文件各字段的提示的响应,以便重复使用相同的响应而不必再次输入它们。格式文件可以提供将数据大容量复制到 SQL Server 实例,或从 SQL Server 实例大容量复制数据时所需的所有格式信息。格式文件提供了一种灵活的系统,用来写入很少需要或不需要编辑即可符合其它数据格式的数据文件,或者用来从其它软件中读取数据文件。 例如,若要交互式地将 publishers 表大容量复制到 Publ.txt 文件中,可以使用以下命令: bcp pubs..publishers out publ.txt -Sservername -Usa -Ppassword 对 publishers 表的每一列,都会出现四个提示,并且在方括号中显示 bcp 提供的默认值。以下示例只是 publishers 表中 pub_id 列的情况。 Enter the file storage type of field pub_id [char]: Enter prefix length of field pub_id [0]: Enter length of field pub_id [4]: Enter field terminator [none]: 按 ENTER 键接受所提供的默认值。若要指定与默认值不同的值,请在命令提示符下输入新值。 请参见 使用格式文件 ©1988-2000 Microsoft Corporation。保留所有权利。
一升米 2017-11-23
  • 打赏
  • 举报
回复
引用 23 楼 xg436 的回复:
又看了一遍,你是可以直接缓存A中所有数据?那就新建个数组,和字符串数组,第一条的id是100,那就int【100】=1,string【0】=内容,然后写b就string【int【i++】】
完全没看懂,缓存了不是还要排序么?
一升米 2017-11-23
  • 打赏
  • 举报
回复
引用 15 楼 ananluowei 的回复:
先读取每条记录的id,记下这条记录在文件中的偏移,用一个结构保存这2个数据。读完整个文件,对这个结构的数组排序。 根据排序后的内容,逐条id读取每条记录,存到新文件。
还有个问题,如果id不是每个数据结构的第一个,这个id是没用的,无法计算偏移的。。。可能id是第6个属性,前五个属性长度无法确定,所以是找不到文件偏移的,是吧??如果找不到的话,这个偏移就不行吧?谢谢
引用 21 楼 zhao4zhong1 的回复:
参考SQL Server中的bcp命令行工具。
数据有规律,就能导入进数据库?谢谢
xg436 2017-11-22
  • 打赏
  • 举报
回复
又看了一遍,你是可以直接缓存A中所有数据?那就新建个数组,和字符串数组,第一条的id是100,那就int【100】=1,string【0】=内容,然后写b就string【int【i++】】
xg436 2017-11-22
  • 打赏
  • 举报
回复
不想用数据库就用文件代替吧。 你的文件是1G的,最大的id是一百万。然后新建100个文本,标记为1-100,然后新建100个字符串,然后读A,id在1-1w的加到第一个字符串里面,id在1-2w的写到第二个...,内存到上限了就写到对应的文本里去。读取完以后,读取文件1到缓存,排序,写到B里,然后是文件2.3.4..。
赵4老师 2017-11-22
  • 打赏
  • 举报
回复
参考SQL Server中的bcp命令行工具。
一升米 2017-11-22
  • 打赏
  • 举报
回复
引用 13 楼 hongwenjun 的回复:
放出 示例的 几行出来看下,可以用不相关 代替文字信息
您是想从数据上找规律么?呵呵,数据上不是按照行分的,例如数据就是,head:id:001 内容:abc 语言:英语 end head:id:002 内容:abc 语言:汉语 end,就是这么一个挨着一个的,通过head头和end尾区分,head和end中间夹着的内容属性一样,没有换行区分。属性我这举例子就3个,实际每个属性也是10几个。
一升米 2017-11-22
  • 打赏
  • 举报
回复
引用 15 楼 ananluowei 的回复:
先读取每条记录的id,记下这条记录在文件中的偏移,用一个结构保存这2个数据。读完整个文件,对这个结构的数组排序。 根据排序后的内容,逐条id读取每条记录,存到新文件。
谢谢了,我想问下如果文件很大的话,再次找到这个偏移时候会不会很慢??再就是对id排序和对从文件中读取的整个数据进行排序,效率上会有很大提升么?感谢了
一升米 2017-11-22
  • 打赏
  • 举报
回复
引用 14 楼 hdt 的回复:
几十万条数据,估计一二百兆,不想用数据库,可以用内存映射文件 具体请查msdn
目前就是用的内存映射文件,读取文件这里的速度应该可以了
hongwenjun 2017-11-22
  • 打赏
  • 举报
回复

我电脑里所有文件 的 全路径 记录 24.66万, list.txt 15.5M 排序的时间
hongwenjun 2017-11-22
  • 打赏
  • 举报
回复
http://bbs.csdn.net/topics/380117760

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <fstream>
#include <sstream>
#define WIN32 //VC 编译器开关
#include <time.h>
#ifdef WIN32
# include <windows.h>
#else
# include <sys\time.h>
#endif
#ifdef WIN32
int gettimeofday(struct timeval* tp, void* tzp)
{
time_t clock;
struct tm tm;
SYSTEMTIME wtm;
GetLocalTime(&wtm);
tm.tm_year = wtm.wYear - 1900;
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay;
tm.tm_hour = wtm.wHour;
tm.tm_min = wtm.wMinute;
tm.tm_sec = wtm.wSecond;
tm. tm_isdst = -1;
clock = mktime(&tm);
tp->tv_sec = clock;
tp->tv_usec = wtm.wMilliseconds * 1000;
return (0);
}
#endif /* WIN32 */
using std::stringstream;
using std::vector;
using std::string;
using std::fstream;
void help(); // 调用使用帮助
bool isdigit(const std::string& str); // overload function 重载函数
bool isalpha(const std::string& str);
stringstream& load_sstream(stringstream& oss, fstream& infile); // 加载文件到sstream
vector<string> & sortVecData(vector<string> & vecData); // 排序和删除重复

int main(int argc, char* argv[])
{
using namespace std;
if ((1 == argc) || (2 == argc)) { //错误输入处理
help();
return -1;
}
string Value = argv[1];
fstream inFile;
inFile.open(argv[2], ios_base::in);
if (!inFile) {
cerr << "文件错误:不能打开输入文件: " << argv[2] << endl << endl ;
help();
return -1;
}
bool sortData = false; // 排序开关
if (argc >= 4 && ('s' == argv[3][1] || 'S' == argv[3][1])) {
sortData = true;
}

stringstream oss;
ofstream ofDataFile;
ofDataFile.open("LineData.txt"); //保存到 新的数据档文件
long ixold = 0, ixnew = 0;
string::size_type pos;
string strLine;
vector<string> vecLine;
cout << "当前搜索关键字:" << Value << endl;
// 取时间断点
timeval tv;
gettimeofday(&tv, NULL);
double cl = tv.tv_sec + (double)tv.tv_usec / 1000000;

while (getline(inFile , strLine)) { // 整行处理,数据文件 数据输入
if (strcmp(argv[1] , "ALL") == 0 ) { // 所有数据排序用
vecLine.push_back(strLine);
} else {
pos = strLine.find(Value);
if (pos != string::npos) {
vecLine.push_back(strLine); //数据记录到容器
}
}

ixold++; // 旧数据计数器
if (ixold % 100000 == 0) cout << ">";
}

gettimeofday(&tv, NULL);
cl = (tv.tv_sec + (double)tv.tv_usec / 1000000) - cl;
cout << "\n原来数据记录数目:" << ixold << "\t";
cout << "新的数据记录数目:" << vecLine.size() << endl;
cout << "提取数据花费时间:" << cl << " 秒\n";

if (sortData) { // 判断排序
cout << "正在排序优化中,请等候....\n";
gettimeofday(&tv, NULL);
cl = tv.tv_sec + (double)tv.tv_usec / 1000000;

sortVecData(vecLine); //数据排序 删除重复
cout << "排序优化后数据量:" << vecLine.size() << "\t";
gettimeofday(&tv, NULL);
cl = (tv.tv_sec + (double)tv.tv_usec / 1000000) - cl;
cout << "排序数据花费时间:" << cl << " 秒\n";
}
cout << "正在把数据写到新文件中......\t如果数据重复,可以最后加参数 -S 优化排序\n" ;
gettimeofday(&tv, NULL);
cl = tv.tv_sec + (double)tv.tv_usec / 1000000;
vector<string>::iterator iter = vecLine.begin();
while (iter != vecLine.end()) {
oss << *iter++ << endl; // 数据写到缓存流
if (ixnew++ % 100000 == 0) cout << "<"; //新数据计数器
}
ofDataFile << oss.str(); // 缓冲区写文件
gettimeofday(&tv, NULL);
cl = (tv.tv_sec + (double)tv.tv_usec / 1000000) - cl;
cout << "保存新数据文件花费时间:" << cl << " 秒\n";
cout << "\n已经生成新的包含关键字的数据文件 LineData.txt" << endl;
inFile.close();
ofDataFile.close();
return 0;
}
// overload function 重载函数 isdigit
bool isdigit(const std::string& str)
{
bool flag = true;
for (std::string::size_type i = 0; i < str.length(); i++) {
while (!isdigit(str[i])) {
flag = false;
break;
}
}
return flag;
}
bool isalpha(const std::string& str)
{
bool flag = true;
for (std::string::size_type i = 0; i < str.length(); i++) {
while (!isalpha(str[i])) {
flag = false;
break;
}
}
return flag;
}
void help()
{
using std::cerr;
cerr << "本工具可以从文件中按关键字搜索数据行 并且有排序功能 [版本 0.98] BY Hong Wenjun\n\n";
cerr << "示例 1 :D:\\>LineExtraction.exe \"关键字\" D:\\原始数据.txt \n" ;
cerr << "示例 2 :D:\\>LineExtraction.exe \"关键字\" D:\\原始数据.txt -S\n" ;
cerr << "示例 3 :D:\\>LineExtraction.exe ALL D:\\原始数据.txt -S\n\n" ;
cerr << "请输入一个关键字!![ \"关键字\" ALL ] 按两种方式选择一种\n";
cerr << "ALL 关键字 提取所有数据,和-S参数配合排序\n";
cerr << "-S 参数 排序优化,删除重复数据\n";
}

stringstream& load_sstream(stringstream& oss, fstream& infile) // 加载文件到sstream
{
oss << infile.rdbuf();
return oss;
}

vector<string> & sortVecData(vector<string> &vecData) // 排序和删除重复 数字序列
{
sort(vecData.begin(), vecData.end()); //数据排序
vector<string>::iterator end_unique = unique(vecData.begin(), vecData.end()); // 移动重复到最后
vecData.erase(end_unique, vecData.end()); //删除重复
return vecData;
}


看你的文件多大 280M的 600W记录某站 数据档,使用这个程序所有记录排序
写文件时,使用大内存缓冲,代价是多使用1G内存
大尾巴猫 2017-11-22
  • 打赏
  • 举报
回复
先读取每条记录的id,记下这条记录在文件中的偏移,用一个结构保存这2个数据。读完整个文件,对这个结构的数组排序。 根据排序后的内容,逐条id读取每条记录,存到新文件。
真相重于对错 2017-11-22
  • 打赏
  • 举报
回复
几十万条数据,估计一二百兆,不想用数据库,可以用内存映射文件 具体请查msdn
hongwenjun 2017-11-22
  • 打赏
  • 举报
回复
放出 示例的 几行出来看下,可以用不相关 代替文字信息
自信男孩 2017-11-22
  • 打赏
  • 举报
回复
引用 10 楼 sys0613 的回复:
[quote=引用 8 楼 cfjtaishan 的回复:] [quote=引用 5 楼 sys0613 的回复:] [quote=引用 2 楼 cfjtaishan 的回复:] 如果将所有的数据从文件里读取,然后再排序,确实很占内存,数据项目多,排序也会比较耗时。 是否可以根据ID的特点,比如ID是从1到多少,先从文件里读,若读到ID为1的,则写入B文件,读到ID位2的写入文件,然后记录一下目前B文件里的ID号,每次写B文件前,先从内存里查找是否已经符合写文件的ID,若没有继续从A文件里读,若读到直接写B文件,若没有继续读; 比如,读到的ID信息的顺序是 3 4 7 8 1,此时将ID位1的信息写入B文件,记录目前B文件里的ID,接下来从已经读到的缓存里查找2,若没有继续从文件里读;知道读到ID为2,写入B文件,然后从已经读到的文件里查找3。。。 这样的好处是,可以减少内存里的消耗,还能减少排序的耗时,但是有个缺点,ID需要符合已经规律; 根据你的ID信息来确定我的方法是否可行了。
谢谢您了,我这个id很大可能是倒序,如果先都存到内存中,每轮进到内存中去扫描,还是相当于将剩下数据扫描了一遍吧?这种场景是不是就效率没变啊?[/quote] 要看你的文件里ID是否位倒序,如果是完全的倒序那么效率没怎么提高 如果是完全的倒序,如果占用内存很多,想用时间换空间,可以考虑写入多个文件,每个文件是有序的。最后再将文件集中到一个文件里。这个会比较麻烦,但也是一种办法[/quote] 谢谢您了,我现在是想用空间换时间,目前执行的太慢了[/quote] 我说反了,写入多个文件是以空间换时间;数据量大,排序会很占时间,另外,你的数据放在局部数组里还是全局数组或者在堆上申请的空间,另外,你的排序是用什么方法,可以考虑用快排或者堆排实现
一升米 2017-11-22
  • 打赏
  • 举报
回复
引用 9 楼 hdt 的回复:
[quote=引用 7 楼 sys0613 的回复:] [quote=引用 6 楼 hdt 的回复:] 个人感觉这样的情况还是把数据导入到数据库里面处理比较好!
就是将文件中的数据都存到数据库中,然后利用数据库进行排序?因为这个id只是一条数据中的一项,一条数据有好多属性,id只是作为排序的关键字了。 唉,不知道不用数据库的话能不能提高点效率呢。好尴尬。今天跑70w数据,直接就没看到结束。。强制停止了。。。[/quote] 如果文本文件的格式比较规范,完全可以通过数据库自带的工具导入到数据库中。几十万数对于像oracle,mssql之类数据库,完全是小case[/quote] 现在是想用最少的软件来完成此项工作,两个文件的交互不想带上数据库。如果纯两个文件来排序,不用数据库的话有什么更好的办法么?谢谢了
一升米 2017-11-22
  • 打赏
  • 举报
回复
引用 8 楼 cfjtaishan 的回复:
[quote=引用 5 楼 sys0613 的回复:] [quote=引用 2 楼 cfjtaishan 的回复:] 如果将所有的数据从文件里读取,然后再排序,确实很占内存,数据项目多,排序也会比较耗时。 是否可以根据ID的特点,比如ID是从1到多少,先从文件里读,若读到ID为1的,则写入B文件,读到ID位2的写入文件,然后记录一下目前B文件里的ID号,每次写B文件前,先从内存里查找是否已经符合写文件的ID,若没有继续从A文件里读,若读到直接写B文件,若没有继续读; 比如,读到的ID信息的顺序是 3 4 7 8 1,此时将ID位1的信息写入B文件,记录目前B文件里的ID,接下来从已经读到的缓存里查找2,若没有继续从文件里读;知道读到ID为2,写入B文件,然后从已经读到的文件里查找3。。。 这样的好处是,可以减少内存里的消耗,还能减少排序的耗时,但是有个缺点,ID需要符合已经规律; 根据你的ID信息来确定我的方法是否可行了。
谢谢您了,我这个id很大可能是倒序,如果先都存到内存中,每轮进到内存中去扫描,还是相当于将剩下数据扫描了一遍吧?这种场景是不是就效率没变啊?[/quote] 要看你的文件里ID是否位倒序,如果是完全的倒序那么效率没怎么提高 如果是完全的倒序,如果占用内存很多,想用时间换空间,可以考虑写入多个文件,每个文件是有序的。最后再将文件集中到一个文件里。这个会比较麻烦,但也是一种办法[/quote] 谢谢您了,我现在是想用空间换时间,目前执行的太慢了
加载更多回复(9)

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧