Perl解释Properties不太方便,需要自己分析
#!/usr/bin/perl -w
package ncs::Properties;
use vars qw(@ISA @EXPORT @EXPORT_OK);
use Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(load get set merge merge_with_file);
use ncs::Common;
sub new
{
my $this = {};
$this->{'properties_file'} = 'ncs_default.properties';
$this->{'properties'} = {};
bless $this;
return $this;
}
sub load
{
my ($class, $properties_file, $keep_escape) = @_;
if (!defined($properties_file) || $properties_file eq ""){
$properties_file = "ncs.properties";
}
$class->{'properties_file'} = $properties_file;
my $k = $v = "";
my $is_multiline = 0;
open(PROPFILE, "<$properties_file") || die("Cannot open properties file $properties_file");
while($line = <PROPFILE>){
$line = trim($line);
#space line or commentted line will be ignored
if($line =~ /^$/ || $line =~ /^#/){
next;
}
if($line =~ /\\$/){ #end with \
$line =~ s/\\$//;
if($is_multiline){ #not first line,not end line
$v = $v.' '.$line;
}
else{ #first line
($k,$v) = split("=", $line, 2);
$is_multiline = 1;
}
next;
}
if($is_multiline){
#print "is multiline: $is_multiline\n";
#print "$line\n";
$v = $v.' '.$line;
if($line =~ /[^\\]$/){ #end line
$class->{'properties'}->{trim($k)} = trim($v);
#print "$k=$v\n";
$k = $v = "";
$is_multiline = 0;
}
next;
}
($k,$v) = split("=", $line, 2);
if(trim($k) eq ""){
next;
}
#print("$k=$v");
$class->{'properties'}->{trim($k)} = trim($v);
$k = $v = "";
}
close(PROPFILE);
my $import = $class->get("ncs.import");
$import = $class->unescapeProperty("ncs.import", $import) if $import;
if(-e $import){
print("found import properties: $import\n");
$class->merge_with_file($import);
}
$class->unescapeProperties() if !$keep_escape;
return $class->{'properties'};
}
sub unescapeProperty
{
my ($class, $k, $v) = @_;
if(!defined($v) || $v =~ /^\s*$/){
return;
}
while($v =~ /^(.*)\${([^}]+)}(.*)$/){ #match ${ncs.log.dir}
my $prefix = (defined($1) ? $1 : "");
my $matched = $2;
my $suffix = (defined($3) ? $3 : "");
#print("matched key: $matched\n");
if(!exists($class->{'properties'}->{$matched})){
warn("[warn] matched key not exists: $matched");
last;
}
#matched key exits
#print("$matched=".$class->{'properties'}->{$matched}."\n");
$class->{'properties'}->{$k} = $prefix.$class->{'properties'}->{$matched}.$suffix;
$v = $class->{'properties'}->{$k};
}
while($v =~ /^(.*)(\$ENV{[^}]+})(.*)$/){ #match $ENV{HOME}
my $prefix = (defined($1) ? $1 : "");
my $matched = $2;
my $suffix = (defined($3) ? $3 : "");
#print("matched env: $matched\n");
$matched = eval($matched) || '';
#print("matched env value:".$matched."\n");
$matched = (defined($matched) ? $matched : "");
$class->{'properties'}{$k} = $prefix.$matched.$suffix;
$v = $class->{'properties'}->{$k};
}
#print("$k=$class->{'properties'}->{$k}\n");
return $v;
}
sub unescapeProperties
{
my $class = shift @_;
my $props = $class->{'properties'};
#print "list properties before unescape start\n";
while( local ($k,$v) = each(%$props)){
#print("$k=$v\n");
$class->unescapeProperty($k, $v);
}
#print "list properties before unescape end\n";
}
sub get
{
my ($class,$key,$default) = @_;
if(exists($class->{'properties'}->{$key})){
return $class->{'properties'}->{$key};
}
if(defined($default)){ #not exists $props{$key}
return $default;
}
return ""; #no default
}
sub set
{
my ($class, $key, $value) = @_;
$class->{'properties'}->{$key} = $value;
}
sub merge
{
my ($class, $props_to_be_merged, $override) = @_;
my $props = $class->{'properties'};
while(local ($k,$v) = each(%$props_to_be_merged)){
$k = trim($k);
if($k eq ""){ next; }
if(exists($props->{$k})){ next if(!$override); }
$props->{$k} = trim($v);
}
return $props;
}
sub merge_with_file
{
my ($class, $file_to_be_merged, $override) = @_;
if(-e $file_to_be_merged){
return $class->merge(ncs::Properties->new()->load($file_to_be_merged, 1), $override);
}
return $class->{'properties'};
}
Ruby也没有提供现成的库,自己写了一个,可以看出,ruby跟perl是何等的相似,尤其是正则表达式的应用这块,难怪网络上都说ruby是perl6,确实不假。
#!/usr/bin/ruby -w
class Properties
def initialize
@properties = {}
end
def load(properties_file, escape=true)
raise "#{properties_file} not exists!" if not File.exists?(properties_file)
@properties_file = properties_file
k = v = ""
is_multiline = false
IO::foreach(@properties_file) {|line|
line = line.strip
#skip blank-space line or comments
next if line =~/^\s*$/ or line =~/^\s*#/
#puts "line: #{line}"
#end with \
if line =~ /\\$/
#puts "line end with \\: #{line}"
line.chomp!().chop!()
if is_multiline #not first line,not end line
v = v + ' ' + line
else #first line
k,v=line.split(/=/, 2)
is_multiline = true
end
next;
end
if is_multiline
#puts "line is one of multiline: #{is_multiline}"
v = v+' '+line
if line =~ /[^\\]$/ #end line
@properties.store(k.strip, v.strip)
#puts "#{k}=#{v}"
k = v = ""
is_multiline = false
end
next;
end
k,v=line.split(/=/, 2)
next if(k.strip.empty?)
#puts "#{k}=#{v}"
@properties.store(k.strip, v.strip)
k = v = ""
}
self.import_files()
self._escapeprops() if escape
return self
end
def _escapeprops
@properties.each do |key,val|
self._escapeprop(key,val)
end
end
def _escapeenv(env)
env = env.delete('$').sub('{','["').sub('}','"]')
#puts "env: #{matched}"
(eval(env) or "")
end
def _escapeprop(key,val)
return if val =~ /^\s*$/ #ignore empty value
while val =~ /^(.*)\$\{([^}]+)\}(.*)$/ #match ${ncs.log.dir}
prefix = $1 or ""; matched = $2; suffix = $3 or ""
#puts "matched key: #{matched}"
if not @properties.key?(matched)
puts "matched key not exists: #{matched}"
break
end
#matched key exits
#puts "#{matched}="+ @properties.fetch(matched)
@properties.store(key, [email protected](matched)+suffix)
val = @properties.fetch(key)
end
while(val =~ /^(.*)(\$ENV\{[^}]+\})(.*)$/) #match $ENV{HOME}
prefix = $1 or ""; matched = $2; suffix = $3 or ""
matched = self._escapeenv(matched)
#puts "matched env value:#{matched}"
#puts "prefix=#{prefix}, matched=#{matched}, suffix=#{suffix}"
@properties.store(key, prefix+matched+suffix)
val = @properties.fetch(key)
end
#puts "#{key}=#{@properties[key]}"
return val;
end
def get(key, default='')
if(@properties.key?(key)): return @properties.fetch(key) end
if(!default.nil?): return default end
end
def getint(key, default=0)
self.get(key,default).to_i
end
def getfloat(key, default=0.0)
self.get(key,default).to_f
end
alias getlong getint
alias getdouble getfloat
def getboolean(key)
return true if self.getint(key, 0) > 0
return false
end
def set(key,value)
@properties.store(key,value)
end
def size
@properties.size
end
def merge(props_to_be_merged, override=false)
props_to_be_merged.each do |k,v|
next if k.strip.empty?
next if @properties.key?(k) and !override
@properties.store(k, v.strip)
end
self
end
def import_files(override=false)
import = self.get("ncs.import")
if defined?(import) and not import.empty?
puts "found import properties: #{import}"
import = self._escapeprop("ncs.import",import)
if not import.nil? and File.exist?(import)
return self.merge(Properties.new.load(import,false).to_hash, override)
end
end
self
end
def to_hash
@properties
end
def keys
@properties.keys
end
def values
@properties.values
end
def dump
keys = @properties.keys.sort
keys.each do |key|
val = self.get(key)
puts "#{key}=#{val}"
end
end
protected :_escapeprop,:_escapeenv,:_escapeprops
end
其实还写了一版python的,也一并贴上来对比一下
#!/usr/bin/env python
from ncs.core.component import Component
from ncs.exceptions import *
import sys
import os
import re
import logging
class PropertiesNotExist(Exception):
def __init__(self, properties_file):
self.properties_file = properties_file
def __str__(self):
return repr("properties file {0} not exist!".format(self.properties_file))
class PropertiesNotLoad(Exception):
def __init__(self, properties_file):
self.properties_file = properties_file
def __str__(self):
return repr("properties file {0} is not loaded!".format(self.properties_file))
class Properties(Component):
"""
This is a python portion for java Properties file
"""
def __init__(self):
Component.__init__(self, 'ncs.core.properties.Properties', ['load','get','set','search'])
self.properties_file = ''
self.raw_properties = {}
self.properties = {}
self.logger = logging.getLogger(self.__class__.__name__)
def load(self, resource):
if not os.path.exists(resource):
self.logger.error("{0} not exists".format(resource))
raise PropertiesNotExist(resource)
if not os.path.isfile(resource):
self.logger.error("{0} is not a file!".format(resource))
raise PropertiesNotLoad(resource)
self.properties_file = resource
self.raw_properties = self.properties = self._parse(resource)
#check if there is import properties or file
import_file = self.get("ncs.import", self.get("import"))
if import_file:
import_file = self._unescape_property(import_file)
if not os.path.exists(import_file):
self.logger.warn("import file {0} not exists".format(import_file))
self.logger.warn("ignored import file {0}".format(import_file))
if self.isdebugon(): print("import properties file: {0}".format(import_file))
self.import_from_file(import_file)
self._unescape_properties()
return self.properties
def _parse(self, resource):
props = dict()
fp = open(resource, 'r')
(k,v) = ('','')
is_multi_line = False
for line in fp.readlines():
line = line.strip()
#space line or commentted line will be ignored
if len(line) == 0 or line.startswith('#'):
continue
#end with character(\), multi-line start line
if not is_multi_line and line.endswith('\\'):
is_multi_line = True
#remove the end character(\) and split with character(=)
(k,v) = line.replace('\\','').split('=', 1)
continue
if is_multi_line:
#not end with character(\), multi-line end line
if not line.endswith('\\'):
v = v + ' ' + line
props[k.strip()] = v.strip()
k = v = ''
is_multi_line = False
#multi-line
else:
v = v + ' ' + line.replace('\\','')
continue
#normail line(key=value)
(k,v) = line.replace('\\','').split('=', 1)
if len(k.strip()) == 0:
continue
props[k.strip()] = v.strip()
(k,v) = ('','')
return props
def _unescape_property(self, value):
#white space
if len(value.strip()) == 0: return
#match ${ncs.log.dir}
extract_vars = lambda s: [v[1] for v in re.findall(r'^(.*)\${([^}]+)}(.*)$', s)]
while extract_vars(value):
for var in extract_vars(value):
if self.isdebugon(): print("matched key: {0}".format(var))
if not self.properties.has_key(var):
print("[warn] matched key not exists: {0}".format(var))
break
#matched key exits
val = self.properties.get(var)
if self.isdebugon(): print("{0} = {1}".format(var, val))
value = value.replace('${'+var+'}', val)
#match $ENV{HOME}
extract_envs = lambda s: [e[1] for e in re.findall(r'^(.*)\$ENV{([^}]+)}(.*)$', s)]
while extract_envs(value):
for env in extract_envs(value):
if self.isdebugon(): print("matched environ: {0} = {1}".format(env, os.getenv(env,'')))
value = value.replace('$ENV{'+env+'}', os.getenv(env,''))
return value
def _unescape_properties(self):
for (key,value) in self.properties.items():
self.properties[key] = self._unescape_property(value)
def get(self, key, default=None):
return self.properties.get(key, default)
def getint(self, key, default=0):
int(self.get(key,default))
def getlong(self, key, default=0):
long(self.get(key,default))
def getfloat(self, key, default=0.0):
float(self.get(key,default))
def getboolean(self, key, default=False):
value = self.get(key)
if value:
return True
return default or False
def getrange(self, key):
result = []
value = self.get(key)
if value:
for val in value.split(','):
#val as range
if '-' in val:
(start,end) = val.split('-', 1)
result.extend(range(int(start),int(end)+1))
else:
result.append(int(val))
return result
def set(self, key, value=None):
self.properties[key] = value
def search(self, keywords):
props = {}
for (key,value) in self.properties.items():
if keywords in key:
props[key] = value
return props
def size(self):
return len(self.properties.items())
def dump(self):
for key in sorted(self.properties.keys()): print("{0} = {1}".format(key,self.properties.get(key)))
def import_properties(self, properties, override=False):
for (key,value) in properties.items():
if self.properties.has_key(key) and not override: continue
self.properties[key] = value
def import_from_file(self, resource, override=False):
properties = self._parse(resource)
self.import_properties(properties, override)