1

我想制作一个包含一些对象的配置文件,像这样(当然没有任何参数可以被视为主键)

param1=abc
param2=ghj

param1=bcd
param2=hjk
; always the sames parameters

这个文件可以被读取,比方说Config::IniFiles,因为它直接转录成 ini 文件,像这样

[0]
param1=abc
param2=ghj

[1]
param1=bcd
param2=hjk

例如,类似的东西

perl -pe 'if (m/^\s*$/ || !$section ) print "[", ($section++ || 0) , "]"'

并以

open my $fh, '<', "/path/to/config_file.ini" or die $!;
$cfg = Config::IniFiles->new( -file => $fh );
(...parse here the sections starting with 0.)

但是,我在这里问我一些关于事情变得相当复杂的问题......

(A) 有没有办法转换 $fh,以便在顺序读取文件之前不需要执行 perl one-liner?因此,在 perl 期间转换文件实际上是在读取它。

或者

(B) 是否有一个模块可以读取我的奇妙平面数据库?或者有什么接近的?我让 myslef 说,Gnu coreutils 会读取这种平面文件,但我不记得是怎么做的了。

4

4 回答 4

1

您可以将修改后的数据存储在真实文件或字符串变量中,但我建议您通过将输入记录分隔符设置为空字符串来使用段落模式。像这样 $/

use strict;
use warnings;

{
  local $/ = '';  # Read file in "paragraphs"
  my $section = 0;
  while (<DATA>) {
    printf "[%d]\n", $section++;
    print;
  }
}

__DATA__
param1=abc
param2=ghj

param1=bcd
param2=hjk

输出

[0]
param1=abc
param2=ghj

[1]
param1=bcd
param2=hjk

更新

如果您将文件读入字符串,如上添加部分标识符,则可以Config::IniFiles使用字符串引用将结果直接读入对象,例如

my $config = Config::IniFiles->new(-file => \$modified_contents)

这个例子显示了tie接口,它产生一个包含配置信息的 Perl 散列。我Data::Dump只用来显示结果哈希的结构。

use strict;
use warnings;

use Config::IniFiles;

my $config;
{
  open my $fh, '<', 'config_file.ini' or die "Couldn't open config file: $!";
  my $section = 0;
  local $/ = '';
  while (<$fh>) {
    $config .= sprintf "[%d]\n", $section++;
    $config .= $_;
  }
};

tie my %config, 'Config::IniFiles', -file => \$config;

use Data::Dump;
dd \%config;

输出

{
  # tied Config::IniFiles
  "0" => {
           # tied Config::IniFiles::_section
           param1 => "abc",
           param2 => "ghj",
         },
  "1" => {
           # tied Config::IniFiles::_section
           param1 => "bcd",
           param2 => "hjk",
         },
}
于 2014-12-18T13:42:54.187 回答
1

您可以使用变量引用而不是文件名来创建从中读取的文件句柄:

use strict;
use warnings;
use autodie;

my $config = "/path/to/config_file.ini";

my $content = do {
  local $/;
  open my $fh, "<", $config;
  "\n". <$fh>;
};

# one liner replacement
my $section = 0;
$content =~ s/^\s*$/ "\n[". $section++ ."]" /mge;

open my $fh, '<', \$content;
my $cfg = Config::IniFiles->new( -file => $fh );
# ...
于 2014-12-18T13:34:14.443 回答
1

您可以创建一个简单的Config::INI::Reader子类:

package MyReader;

use strict;
use warnings;

use base 'Config::INI::Reader';

sub new {
    my $class = shift;
    my $self = $class->SUPER::new( @_ );

    $self->{section} = 0;

    return $self;
}


sub starting_section { 0 };

sub can_ignore { 0 };

sub parse_section_header {
     my ( $self, $line ) = @_;

    return $line =~ /^\s*$/ ? ++$self->{section} : undef ;
}

1;

根据您的输入,这给出:

% perl -MMyReader -MData::Dumper -e 'print Dumper( MyReader->read_file("cfg") )'
$VAR1 = {
          '1' => {
                   'param2' => 'hjk',
                   'param1' => 'bcd'
                 },
          '0' => {
                   'param2' => 'ghj',
                   'param1' => 'abc'
                 }
        };
于 2014-12-18T15:26:14.230 回答
0

您可能希望对对象的通量(如 Powershell)而不是文本的通量执行操作,所以

use strict; 
use warnings; 
use English;

sub operation {
    # do something with objects
    ...
}

{
local $INPUT_RECORD_SEPARATOR = '';
# object are separated with empty lines
while (<STDIN>) {
    #                  key       value
    my %object = ( m/^ ([^=]+) = ([[:print:]]*) $ /xmsg ); 
    # key cannot have = included, which is the delimiter
    # value are printable characters (one line only) 
    operation ( \%object )  
} 

和其他答案一样。

于 2014-12-18T17:18:30.817 回答