免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4418 | 回复: 8
打印 上一主题 下一主题

split内存占用太大怎么办 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-11-14 19:52 |只看该作者 |倒序浏览
现在有图1所示文件,包含了数百万个位点的信息,相互之间用空格分开。
现在需要找出指定开始和终止位置之间的信息,统计有多少是大于3的。
比如需要统计第3000-5000个位点内,有多少个位点的数字是大于3.
如果用split,内存会飙到十多G,不适用。substr也不好用,因为如果数字为两位数或者三位数,则会占用多个字符位置,没办法统计。
请问应该怎么解决这个问题?

1.jpg (177.82 KB, 下载次数: 103)

图1

图1

论坛徽章:
7
戌狗
日期:2013-12-15 20:43:38技术图书徽章
日期:2014-03-05 01:33:12技术图书徽章
日期:2014-03-15 20:31:17未羊
日期:2014-03-25 23:48:20丑牛
日期:2014-04-07 22:37:44巳蛇
日期:2014-04-11 21:58:0915-16赛季CBA联赛之青岛
日期:2016-03-17 20:36:13
2 [报告]
发表于 2013-11-15 01:18 |只看该作者
本帖最后由 rubyish 于 2013-11-14 21:19 编辑

example:
  1. #!/usr/bin/perl

  2. my $data = '0 0 0 1 2 3 4 5 6 7 8 1 1 1 11 15 16 0 0 0 0 12';
  3. my ( $begin, $end, $num, $count ) = ( 5, 15, 3, 0 );
  4. my ( $L1, $L2 ) = ( $begin - 1, $end - $begin + 1 );
  5. my ($wanted) = $data =~ /^(?:\S+\s+){$L1}((?:\S+\s+){$L2})/;

  6. $& > $num and $count++ while $wanted =~ /\S+/g;
  7. print "P$begin ~ P$end [ data > $num ] count = $count\n";
复制代码

论坛徽章:
33
荣誉会员
日期:2011-11-23 16:44:17天秤座
日期:2014-08-26 16:18:20天秤座
日期:2014-08-29 10:12:18丑牛
日期:2014-08-29 16:06:45丑牛
日期:2014-09-03 10:28:58射手座
日期:2014-09-03 16:01:17寅虎
日期:2014-09-11 14:24:21天蝎座
日期:2014-09-17 08:33:55IT运维版块每日发帖之星
日期:2016-04-17 06:23:27操作系统版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-24 06:20:0015-16赛季CBA联赛之天津
日期:2016-05-06 12:46:59
3 [报告]
发表于 2013-11-15 08:36 |只看该作者
回复 1# raoweijian

不是太清楚楼主的数据是来自文件还是哪里. 另外, 数据的总量有多大.

如果数据能读入内存(全部或一行), 可以使用split, 不过, 要加个参数, 类似

  1. my $others = $data;
  2. my ($first, $others) = split(/\s+/, $others, 2);
复制代码
这样, 你的数据会分成两部分, 前一部分只有一个数字, 所有的其它的数据都在另一部分, 这样, 占用的内存应该不是太大. 然后, 继续split下去就行了. 每次只处理一个数据.
   

论坛徽章:
145
技术图书徽章
日期:2013-10-01 15:32:13戌狗
日期:2013-10-25 13:31:35金牛座
日期:2013-11-04 16:22:07子鼠
日期:2013-11-18 18:48:57白羊座
日期:2013-11-29 10:09:11狮子座
日期:2013-12-12 09:57:42白羊座
日期:2013-12-24 16:24:46辰龙
日期:2014-01-08 15:26:12技术图书徽章
日期:2014-01-17 13:24:40巳蛇
日期:2014-02-18 14:32:59未羊
日期:2014-02-20 14:12:13白羊座
日期:2014-02-26 12:06:59
4 [报告]
发表于 2013-11-15 10:11 |只看该作者
本帖最后由 jason680 于 2013-11-15 10:22 编辑

回复 1# raoweijian

How about this example ...
Note: you can control them by yourself

$ cat get_long_string.pl
#!/usr/bin/perl
use strict;
use warnings;

$_="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80";

my $sCnt = 1;
my $sOut = "";
while(m/((?:\S+\s+){10})/g){ # 10 items for one times
  if(31 <= $sCnt && $sCnt <= 50){
    $sOut .= $1
  }
  $sCnt +=10;   # 10 items for one times
}
print "Out=$sOut\n";

$ perl get_long_string.pl
Out=31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
   

论坛徽章:
0
5 [报告]
发表于 2013-11-15 11:30 |只看该作者
try~ set $/:
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. my $str = join(' ' => 1..22);

  5. print $str,"\n";
  6. {
  7.   open my $fh, '<', \$str or die $!;
  8.   local $/=' ';
  9.   while ( <$fh> ) {
  10.     print "$_\n";
  11.   }
  12. }
复制代码

论坛徽章:
0
6 [报告]
发表于 2013-11-15 13:28 |只看该作者
  1. use strict;
  2. use warnings;

  3. $_="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80";

  4. my ($start,$end,$dataStart,$i)=(31,50,0,0);
  5. my @myList;

  6. while(/\s+/g)
  7. {
  8.         $i++;
  9.         push(@myList,substr($_,$dataStart,pos($_)-$dataStart-1)) if($i>=$start&&$i<=$end);
  10.         $dataStart=pos($_);
  11. }

  12. print join(",",@myList),"\n";
复制代码
這個如何

论坛徽章:
5
丑牛
日期:2014-01-21 08:26:26卯兔
日期:2014-03-11 06:37:43天秤座
日期:2014-03-25 08:52:52寅虎
日期:2014-04-19 11:39:48午马
日期:2014-08-06 03:56:58
7 [报告]
发表于 2013-11-15 13:44 |只看该作者
叹为观止{:2_178:}

论坛徽章:
0
8 [报告]
发表于 2013-11-15 14:42 |只看该作者
看来还是我对正则匹配了解得不够。这个数据一共有200M,大约3000W个位点,3楼说的方法每次处理一个似乎并不适用。
二楼和四楼都用到了/^(?:\S+\s+){$L1}((?:\S+\s+){$L2})/类似这样的匹配,不过我不太看得懂。
五楼把默认换行符改成了空格,应该是可行的,不过我还没具体试过。
六楼的方法之前没用到过,代码测试结果很好,就是while(/\s+/g),这种用法具体表示什么不是很明白。

后来我自己想了个办法,就是每次只读100个数字,然后判断是否在区间内,反复循环,效率也还可以接受。

论坛徽章:
7
戌狗
日期:2013-12-15 20:43:38技术图书徽章
日期:2014-03-05 01:33:12技术图书徽章
日期:2014-03-15 20:31:17未羊
日期:2014-03-25 23:48:20丑牛
日期:2014-04-07 22:37:44巳蛇
日期:2014-04-11 21:58:0915-16赛季CBA联赛之青岛
日期:2016-03-17 20:36:13
9 [报告]
发表于 2013-11-16 02:17 |只看该作者
kk861123 发表于 2013-11-15 07:30
try~ set $/:
  1. local $/=' ';
复制代码


学习了!这个速度应该比较快。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP