Perl与Ruby解析Properties文件之异同
by liujun.1980
at 2011-06-14 15:38:34
original http://liumaodou.iteye.com/blog/1087844
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, prefix+@properties.fetch(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)
<br><br>
<span style="color:red">
<a href="http://liumaodou.iteye.com/blog/1087844#comments" style="color:red">已有 <strong>0</strong> 人发表留言,猛击->><strong>这里</strong><<-参与讨论</a>
</span>
<br><br><br>
ITeye推荐