Perl与Ruby解析Properties文件之异同

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)
	

你可能感兴趣的:(python,OS,perl,Ruby,FP)