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

2011-09-08 21:19:15 +08:00
 icerunz
在两个手机之间倒腾通讯录,结果WM这边出来的是一个csv文件,经过处理之后形成一个集合了1000多个联系人的vcf文件,目标手机只认单个vcf文件(每个联系人一个单独的vcf)。于是就想用正则从多个联系人集合的那个vcf中匹配出来之后另存为单个vcf。

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

BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;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=UTF-8;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才匹配出了一个,查了半天不知道什么原因,特请教


不胜感激。
4835 次点击
所在节点    问与答
23 条回复
icerunz
2011-09-08 21:19:47 +08:00
是不是换行符一类的匹配出错的原因?
Livid
2011-09-08 21:21:54 +08:00
用一个循环逐行处理吧。
manhere
2011-09-08 21:31:26 +08:00
gawk for windows
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
2011-09-08 21:37:42 +08:00
print 前面的缩进咋没了,囧
icerunz
2011-09-08 21:40:44 +08:00
@Livid 求教,逐行处理的大概思路是?
icerunz
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
2011-09-08 21:44:09 +08:00
@icerunz 我是用 Python 的那个 re 的,PHP里有多个regex库,不是每个用法和 Python 那个一样的,注意,最前最后的 // 表示的是这里面是一个正则表达式
icerunz
2011-09-08 21:47:20 +08:00
不好意思,我看错一个地方,用
$ereg = "|BEGIN:VCARD([\s\S]+?)END:VCARD|";
就解决了。

原来写的中间部分是([\s\S]+),后来查了书试着写了个?在里面,成功匹配了。但是还是不大清楚其中的原理……求教……
icerunz
2011-09-08 21:49:44 +08:00
@bhuztez 恩PCRE版本的都有个起始符和结束符。
我现在有点搞不清[\s\S]+?和[\s\S]??的区别
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
2011-09-08 22:17:24 +08:00
@Hyperion

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

= =+ 修正一下
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
2011-09-09 00:02:34 +08:00
@noahasm 传说中处理文本牛逼的Perl……
icerunz
2011-09-09 00:03:19 +08:00
@noahasm 能分段讲解一下不
icerunz
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
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
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
2011-09-09 22:13:17 +08:00
@icerunz 换行符代表一行结束,下起新行,也就是说程序是根据换行符来断行的,在碰到换行符前的所有内容,包括换行符本身算作“一行”内容,和你理解的一样。

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

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

其实这些功能,其他语言也都能做到,比如 python, ruby 等,只不过我用 Perl 最顺手,最熟悉
icerunz
2011-09-09 22:56:09 +08:00
@noahasm 谢谢!了解了。开始学习文本处理方面的内容,还有很大差距啊。
你用几种语言?Perl只用作文本方面的处理吧?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/17892

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX