`

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, 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)
	
0
0
分享到:
评论

相关推荐

    perl解析excel文件

    在这个场景中,我们关注的是如何使用Perl来解析Excel文件。Excel文件通常包含表格数据,这对于数据分析、报告生成或自动化工作流程非常有用。Perl提供了多种库来处理这种任务,其中最常用的是Spreadsheet::ParseXLSX...

    TDDOWNLOAD\Perl 与 Python 之间的一些异同

    标题和描述概述的知识点主要集中在Perl与Python的对比分析,涉及两者的起源、特性、哲学理念,以及在基本数据类型上的差异。以下是对这些知识点的深入解析: ### Perl与Python的起源和特点 #### Perl - **起源**...

    perl写的makefile解析器

    Pod是Perl的内置文档格式,与Makefile解析无关,但它是Perl社区常用的文档编写方式。这个模块可能提供了一种解析Pod文档到其他格式(如HTML、man页等)的工具。 总的来说,Perl编写的Makefile解析器对于理解和优化...

    PERL解析IP地址为所在地

    在这个案例中,Perl程序可能封装了读取和查询QQWry数据库的过程,这可能涉及到了文件I/O和字符串解析技巧。 在安装和使用这个Perl包时,可能会有一些依赖条件需要满足。常见的Perl依赖包括CPAN(Comprehensive Perl...

    Perl之读取文本文件

    ### Perl之读取文本文件知识点解析 #### 一、Perl简介 Perl是一种广泛使用的脚本语言,特别适合于文本处理和快速原型开发。它具备强大的正则表达式支持能力,可以方便地处理字符串数据。 #### 二、读取文本文件的...

    perl的xml::simple解析文件

    Perl中的XML::Simple模块是Perl社区广泛使用的XML解析器,尤其适合处理小型或结构简单的XML文档。这个模块的名称虽然包含“Simple”,但它实际上提供了一种简洁的接口,用于将XML数据转换为Perl数据结构,反之亦然。...

    Maltab脚本(2020a)-解析asc文件Can信号(源码)

    该脚本用于解析asc文件并筛选指定can信号,输出到csv文件中。 1、该版本已部分优化,使用m语言以及perl语言编程,大幅提高效率; 2、资源为源码,附有部分注释; 3、脚本可以同时解析多个asc文件,命名方式为0.asc、...

    最好用的Lua,Python,Perl,Ruby,NSIS开发编辑器

    标题提到的“最好用的Lua,Python,Perl,Ruby,NSIS开发编辑器”显然是一款集成了多种编程语言支持的高效工具,它旨在为使用这些语言的开发者提供便利。这款编辑器可能包含了丰富的特性,如语法高亮、代码提示、调试...

    perl5.10.1安装文件

    perl5.10.1安装文件perl5.10.1安装文件perl5.10.1安装文件perl5.10.1安装文件perl5.10.1安装文件perl5.10.1安装文件perl5.10.1安装文件

    perl读写xml文件

    这篇博客文章“perl读写xml文件”很可能是关于如何在Perl中操作XML文件,包括解析XML内容、提取数据、修改数据以及生成新的XML文件。 Perl提供了多个模块来处理XML,最常用的是XML::Simple和XML::LibXML。XML::...

    perl 下dmake 所需要的 安装文件

    然后,下载与Perl版本兼容的`dmake`源码或者预编译的二进制包。由于这里提供的压缩包名为`site`,我们可以推测其中可能包含了已经编译好的`dmake`二进制文件和其他必要的支持文件。解压缩后,将整个`site`目录覆盖到...

    用 perl 实现文件上传

    在IT领域,特别是Web开发中,文件上传是一个常见的需求,涉及到客户端与服务器之间的数据交互。在给定的文件信息中,我们看到了一个基于Perl语言实现的文件上传解决方案,包括前端HTML表单设计和后端Perl脚本处理。...

    Perl实现文件及数据库访问

    标题“Perl实现文件及数据库访问”揭示了这个主题主要关注如何使用Perl编程语言来操作文件以及与数据库进行交互。Perl是一种强大的脚本语言,尤其在处理文本和系统管理任务方面非常出色。在这个主题中,我们将深入...

    perl获取文件修改时间

    3. **跨平台兼容性**:虽然Perl具有良好的跨平台特性,但在不同操作系统中,文件的时间戳可能有所不同(如Windows与Unix/Linux之间)。在编写涉及文件时间戳的脚本时,确保其在目标平台上正确运行是非常重要的。 ...

    ruby程序安装文件

    Ruby的语法设计深受Perl和Smalltalk的影响,具有以下特点: 1. **动态类型**:Ruby是动态类型的,这意味着变量的数据类型是在运行时确定的,无需提前声明。这使得编程更为灵活,但也可能导致一些运行时错误。 2. *...

    perl替换脚本(用于替换文本文件中的单词)

    在这个场景中,我们有一个Perl替换脚本,它被设计用来方便地在文本文件中批量替换特定的单词。下面将详细介绍这个脚本的工作原理和使用方法。 首先,我们需要理解Perl脚本的基本结构。Perl脚本通常包含变量定义、...

    perl创建xlsx文件

    可能太乱了,但是至少可以提供给大家一个范例,使用perl创建xlsx(2007)、读文本文件然后抓取数据生成xlsx文件(具体内容是ezplan提取的drill信息的文本,然后排序 由小到大,第一行指定0.125,第二行0.4,pre放在...

    perl文件用法

    /usr/bin/perl`或类似路径开头,这是Shebang行,告诉系统使用哪个解释器来执行文件。 - 接下来,可以使用`use strict;`和`use warnings;`来开启严格的语法检查和警告,有助于编写更健壮的代码。 - 之后,可以定义...

    perl 自动生成filelist文件脚本

    使用方法简单,在当前目录下,使用perl make_f.pl xx,xx为例化的filelist文件名,按下回车将会生成.f文件,里面列出了当前目录下所有.v文件的绝对路径,将脚本中.v替换成.c也可以检索当前目录下的所有.c文件,...

    Config-Perl:Perl扩展以解析用Perl编写的配置文件

    "Config-Perl"是一个Perl模块,专门设计用来解析那些用Perl语法编写而非传统ASCII格式(如INI或XML)的配置文件。这个模块允许开发者使用Perl的全部语法灵活性来定义配置结构,而不仅仅是简单的键值对。 Config-...

Global site tag (gtag.js) - Google Analytics