perl中处理yaml入门
2013-04-15 22:06:18 阿炯

YAML标记语言,是一个可读性高,用来表达资料序列的编程语言。它参考了其它标记语言(JSON、XML、SDL)的方法及风格,从而形成了自己的语言风格;它的语法和其他高阶语言类似,并且可以简单表达列表、散列,标量等数据形态。它使用空白符号缩排和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种设定档、倾印除错内容、文件大纲等。

在Perl中读取YAML文件也很简单。常用的模块就是YAML,这是个纯Perl的实现,速度慢,但可以跨平台运行。如果追求运行速度而不在乎平台问题,可以使用YAML::Syck和YAML::XS,两者都是C语言的实现。 YAML::Syck的C语言部分基于libsyck,而YAML::XS的C语言部分基于libyaml。两者相比,YAML::XS稍稍快一点,而且它精确地实现了YAML标准1.1版的内容。

Perl对yaml的支持要比其它语言要好,在perl的世界里随处可见,CPAN上新型的软件包里的配置文件皆以它来写成(META.yml)。本文对日常编程中使用yaml格式做简单介绍和总结,以供参考。

在介绍之前,先要理解perl中基本的数据类型:列表、数组、哈希,或者参考本站的Perl入门类文章。

列表:YAML提供缩排/区块(block format)以及内置(inline)两种格式,来表示清单和杂凑表,以下展示几种YAML的基本数据。
--- # 用户列表(区块式)
- hto
- tom
- jack

--- # 内置式
['hto','tom','jack']

哈希:键值由冒号及空白字符分开。
--- # 用户列表(区块式)
name: hto
age: 29

--- # 内置式
{name: hto,age: 29}

注:字串不需要包在引号之内。前方的引领空白符号(leading white space)必须排成条状,以便和其他资料或是行为(如范例中的缩排)明显区分,同级间的空白数量必须相同。

阶层化:
hash的使用:
- {name: John Smith, age: 33}
- name: Mary Smith
  age: 27
 
list的使用:
men: [John Smith, Bill Jones]
women:
  - Mary Smith
  - Susan Williams


代码示例:
use YAML::XS;
use Data::Dumper;

#载入并初始化配置文件
my $mainconf=YAML::XS::LoadFile('/var/www/freeoa/config/main.yaml');
print Dumper($mainconf);

这里变量'$mainconf'是一个hashref,不再是一个简单的变量。访问其中的键值(可能还包含其它数据类型)时,需要用下列的方法:

@{ $ref }       # The whole thing
${ $ref }[$i]   # One element
$ref->[$i]      # One element
@{ $ref }[@i]   # Array slice

yaml:
names: ['hto','tom','jack']

so you can access the array using
@{ $mainconf->{names} }

You get:
print "The names are ", join(', ', @{ $mainconf->{names} }), "\n";

You might also be interseted in
for my $ip (@{$mainconf->{names}}) {
 print $ip . "\n";
}

If all you need to do is print, you can just use join():

print join("\n", @{$mainconf->{names}});

来本站的配置文件来作示例吧,其内容大致如下:
---------------
---
site:
 keywords: unix及linux技术支持 perl及shell编程 开源软件分享和交流 debian gnu/linux
 domain: freeoa.net
 version: 0.2
 ses_cok_time: +15m
 path:
  js: /js
  css: /css
  image: /imgs

db:
 driver: mysql
 db: freedb
 user: user
 password: password
 host:
  pdb: 127.0.0.1
  sdb: 127.0.0.1
 
memcache:
 servers:
  - address: '127.0.0.1:11211'
    weight: 2
  - address: '/tmp/memcached.sock'
    noreply: 1
 namespace: site
 ketama_points: 150
 utf8: '($^V ge v5.8.1 ? 1:0)'

login_terminal:
 title: '登录终端统计'
 field:
  pc: {alias: 'pc',desc: '个人电脑'}
  android: {alias: 'android',desc: '智能手机'}
  iphone: {alias: 'iphone',desc: '苹果设备'}
  web: {alias: 'web',desc: '网页浏览器'}

---------------

当使用'YAML::XS::LoadFile'导入文件后,会返回一个hashref的hash数组,其键分别为:memcache、db、site。
简单的查询函数:
sub get_main_qsh{
 my $qsh=shift;
 return $mainconf->{$qsh};
}

返回Hash
这里取得'db'的配置:get_main_qsh(db)
$VAR1 = {
 'password' => 'password',
 'db' => 'freedb',
 'user' => 'user',
 'host' => {
  'pdb' => '127.0.0.1',
  'sdb' => '127.0.0.1'
  },
 'driver' => 'mysql'
};

这里'host'下定义了两台主机,因些'host'又是一个hashref,同理'site'下面的'path'亦是同理,'login_terminal'下的'field'的每一项都是hashref。

返回Array
yaml:
sms:
 title: '短信用量使用'
 tels: ['131620800abc','13524515def']
 misc: {'rate': '2','period': [8,10,12,14,16,18,20],'unit': 'h'}

'sms'下的'tels'及'misc'下的'period'就是典型的数组结构。
my $rc=get_main_qsh('sms');
$rc->{tels}->[0]; #取得'tels'数组的第一个值。
$rc->{misc}->{period}->[2]; #取得'12'。

组合的hash of array
'memcache->servers'就是一个hash of array of hash:
[{
 'weight' => 2,
 'address' => '127.0.0.1:11211'
 },
 {
 'noreply' => 1,
 'address' => '/tmp/memcached.sock'
 }
];

当然,如何你愿意,也可以将perl里各种数据反解为yaml格式的数据,示例如下:
my %y = (
 foo => 1,
 bar => [qw/one two three/],
 h=>{
 a=>1,b=>3,f=>7
 },
 servers=>[   
 { address => 'localhost:11211', weight => 2.5 },
 '192.168.254.2:11211',
 { address => '/path/to/unix.sock', noreply => 1 } ],
);

print Dump(\%y);

---
bar:
- one
- two
- three
foo: 1
h:
  a: 1
  b: 3
  f: 7
servers:
- address: localhost:11211
  weight: 2.5
- 192.168.254.2:11211
- address: /path/to/unix.sock
  noreply: 1


参考:
Mini-Tutorial: Dereferencing Syntax

YAML