请教一个正则表达式,从vcf文件中匹配信息 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
icerunz
V2EX    问与答

请教一个正则表达式,从vcf文件中匹配信息

  •  
  •   icerunz 2011-09-08 21:19:15 +08:00 5388 次点击
    这是一个创建于 5234 天前的主题,其中的信息可能已经有所发展或是发生改变。
    在两个手机之间倒腾通讯录,结果WM这边出来的是一个csv文件,经过处理之后形成一个集合了1000多个联系人的vcf文件,目标手机只认单个vcf文件(每个联系人一个单独的vcf)。于是就想用正则从多个联系人集合的那个vcf中匹配出来之后另存为单个vcf。

    上面嗦了半天也不直观,也就是说从类似下面的这个文件中匹配出单个的联系人信息:

    BEGIN:VCARD
    VERSION:2.1
    N;charset=;ENCODING=QUOTED-PRINTABLE:=E9=98=BF=E6=97=BA=E5=93=E9=99=A2
    TEL;CELL:13500000000
    END:VCARD

    BEGIN:VCARD
    VERSION:2.1
    N;charset=;ENCODING=QUOTED-PRINTABLE:=E9=B4=E5=93=A5
    TEL;CELL:13700000000
    END:VCARD


    也就是以BEGIN:VCARD开头,END:VCARD结尾的信息。
    我用PHP写,现在用的表达式是:

    $ereg = "|BEGIN:VCARD([\s\S]+?)END:VCARD|";

    这样的话结果是两个数组,每个数组里面包含了400多个联系人信息。。。也就是说跨越了很多的END:VCARD才匹配出了一个,查了半天不知道什么原因,特请教


    不胜感激。
    23 条回复    1970-01-01 08:00:00 +08:00
    icerunz
        1
    icerunz  
    OP
       2011-09-08 21:19:47 +08:00
    是不是换行符一类的匹配出错的原因?
    Livid
        2
    Livid  
    MOD
    PRO
       2011-09-08 21:21:54 +08:00
    用一个循环逐行处理吧。
    manhere
        3
    manhere  
       2011-09-08 21:31:26 +08:00
    gawk for windows
    bhuztez
        4
    bhuztez  
       2011-09-08 21:36:15 +08:00
    regex是可以的吧,

    /(BEGIN:VCARD\n((?!END:VCARD)[^\n]*\n)*END:VCARD)/

    临时用 Python 写了下

    #!/usr/bin/env python2

    import sys, re

    data = sys.stdin.read()

    for m in re.findall(r'(BEGIN:VCARD\n((?!END:VCARD)[^\n]*\n)*END:VCARD)', data, re.M):
    print '**********'
    print m[0]
    bhuztez
        5
    bhuztez  
       2011-09-08 21:37:42 +08:00
    print 前面的缩进咋没了,
    icerunz
        6
    icerunz  
    OP
       2011-09-08 21:40:44 +08:00
    @Livid 求教,逐行处理的大概思路是?
    icerunz
        7
    icerunz  
    OP
       2011-09-08 21:42:34 +08:00
    @bhuztez
    这个/(BEGIN:VCARD\n((?!END:VCARD)[^\n]*\n)*END:VCARD)/
    用preg_match_all();
    出来的结果是:
    array(3) { [0]=> array(0) { } [1]=> array(0) { } [2]=> array(0) { } }
    bhuztez
        8
    bhuztez  
       2011-09-08 21:44:09 +08:00
    @icerunz 我是用 Python 的那个 re 的,PHP里有多个regex库,不是每个用法和 Python 那个一样的,注意,最前最后的 // 表示的是这里面是一个正则表达式
    icerunz
        9
    icerunz  
    OP
       2011-09-08 21:47:20 +08:00
    不好意思,我看错一个地方,用
    $ereg = "|BEGIN:VCARD([\s\S]+?)END:VCARD|";
    就解决了。

    原来写的中间部分是([\s\S]+),后来查了书试着写了个?在里面,成功匹配了。但是还是不大清楚其中的原理……求教……
    icerunz
        10
    icerunz  
    OP
       2011-09-08 21:49:44 +08:00
    @bhuztez 恩PCRE版本的都有个起始符和结束符。
    我现在有点搞不清[\s\S]+?和[\s\S]??的区别
    Hyperion
        11
    Hyperion  
       2011-09-08 22:13:46 +08:00
    @icerunz +一次以上, ?是一次或者零次, 那么就推出...

    [a]+? 尽可能少的重复, 至少0次(空位算进去了).
    "aabaaa" => array("a", "a", "a", "a", "a");

    [a]?? 尽可能少的重复, 至少0次(空位算进去了).
    "aaabaa" => array("", "a", "", "a", "", "a", "", "", "a", "", "a", "");

    ??匹配起来太奇怪, 我从来没有用过...
    Hyperion
        12
    Hyperion  
       2011-09-08 22:17:24 +08:00
    @Hyperion

    [a]+? 至少1次, 尽可能少的重复 = [a]{1}
    "aabaaa" => array("a", "a", "a", "a", "a");

    = =+ 修正一下
    noahasm
        13
    noahasm  
       2011-09-08 22:21:38 +08:00
    楼主建个临时目录 tmpvcf, 假设大 vcf 文件在其中,名为 all.vcf, 如下命令:


    perl -ne 'BEGIN{$/="END:VCARD"} s/^\s+[\r\n]+//; /\w/ && qx/echo "$_" > ${.}.vcf/' all.vcf


    应该就 ok 了
    icerunz
        14
    icerunz  
    OP
       2011-09-09 00:02:34 +08:00
    @noahasm 传说中处理文本牛逼的Perl……
    icerunz
        15
    icerunz  
    OP
       2011-09-09 00:03:19 +08:00
    @noahasm 能分段讲解一下不
    icerunz
        16
    icerunz  
    OP
       2011-09-09 00:04:15 +08:00
    @Hyperion

    表5.懒惰限定符
    *? 重复任意次,但尽可能少重复
    +? 重复1次或更多次,但尽可能少重复
    ?? 重复0次或1次,但尽可能少重复
    {n,m}? 重复n到m次,但尽可能少重复
    {n,}? 重复n次以上,但尽可能少重复

    这个资料还是不错的: http://www.cnblogs.com/deerchao/archive/2006/08/24/zhengzhe30fengzhongjiaocheng.html
    noahasm
        17
    noahasm  
       2011-09-09 12:25:45 +08:00
    @icerunz 如下:


    perl -ne 'BEGIN{$/="END:VCARD"} s/^\s*[\r\n]+//; /\w/ && qx/echo "$_" > ${.}.vcf/' all.vcf

    && 就是 and, 左边成立右边才求值(运行)
    qx// 进行系统调用,和 system 或反引号功能类似,
    ${.} 其实就是 $., 花括号只是为了和后面 .vcf 的点区分开来

    上面的单行命令等效于 perl 程序:

    #!/usr/bin/env perl
    my $file = shift; # shift 会取得命令行的第一个参数
    $/ = "END:VCARD"; # $/ 指换行符,设为END:VCARD后,perl会以它为每行的结束标记,而不是默认的"\n"
    open(F, $file) or die $!;
    while (<F>) {
    next unless /\w/; # 如果当前行不含a-zA-Z0-9_这些字符,直接跳过
    s/^\s*[\r\n]+//; # 删除空白内容
    my $line_num = $.; # $. 指当前行号
    system "echo \"$_\" > $line_num.vcf"; # system 调用,通过 echo 写入文件
    }
    close F;

    把这段 perl 程序寸成 t.pl, 然后运行:

    perl t.pl all.vcf

    效果是一样的.
    icerunz
        18
    icerunz  
    OP
       2011-09-09 21:26:28 +08:00
    @noahasm 非常感谢,Perl处理文本真的不是一般NB,再仔细看看。
    然后有个疑问,这一句:

    $/ = "END:VCARD"; # $/ 指换行符,设为END:VCARD后,perl会以它为每行的结束标记,而不是默认的"\n"

    中提到的制定END:VCARD为换行符,那是否就表示每一个区块当中在END:VCARD之前的/n都会被忽略掉,而以END:VCARD作为行结尾,整个信息块就被作为一行来处理?

    .号在Perl里面有特定的含义么?
    noahasm
        19
    noahasm  
       2011-09-09 22:13:17 +08:00
    @icerunz 换行符代表一行结束,下起新行,也就是说程序是根据换行符来断行的,在碰到换行符前的所有内容,包括换行符本身算作“一行”内容,和你理解的一样。

    . 号在 Perl 里如果不加引号什么的,被称为句点操作符,一般用来连接字符串。
    在正则里它是一个元字符,具体可查 http://perldoc.perl.org/perlretut.html

    $. 是 Perl 的特殊变量,代表当前读入行的行号。

    其实这些功能,其他语言也都能做到,比如 python, ruby 等,只不过我用 Perl 最顺手,最熟悉
    icerunz
        20
    icerunz  
    OP
       2011-09-09 22:56:09 +08:00
    @noahasm 谢谢!了解了。开始学习文本处理方面的内容,还有很大差距啊。
    你用几种语言?Perl只用作文本方面的处理吧?
    args
        21
    args  
       2011-09-09 23:49:20 +08:00
    正则表达是默认是贪婪模式。
    noahasm
        22
    noahasm  
       2011-09-10 02:36:23 +08:00
    @icerunz 我最熟的就是 perl,在学 python/objc, 我用 perl 做各种事情,web应用,图表,爬虫,数据库,文本处理,网络通信等
    icerunz
        23
    icerunz  
    OP
       2011-09-10 12:52:38 +08:00
    @noahasm 恩,领教了。看来语言并不是阻碍应用的直接原因。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5416 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 41ms UTC 03:13 PVG 11:13 LAX 19:13 JFK 22:13
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86