【问题标题】:Read ini files without section names读取没有节名的ini文件
【发布时间】:2015-02-17 07:12:22
【问题描述】:

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

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 做这种平面文件读取,但我不记得是怎么做的。

【问题讨论】:

  • 你想要完成什么?
  • 使用一个包含多个对象的简单配置文件。当然,我有更详细的解决方案,例如 xml、json、...
  • @MUYBelgium:你不能从更标准的ini文件格式开始有什么原因吗?

标签: perl ini gnu-coreutils


【解决方案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'
                 }
        };

【讨论】:

  • 虽然这可以解决上述问题,但要完成一些非常简单的事情需要大量的代码。而且它所做的只是模拟一个缺少节标题的普通.ini 文件,这一点远非显而易见。我相信正确的答案在别处,但这取决于 OP 没有解释的奇怪文件格式的来源。
  • 是的,这有点矫枉过正,但它确实解决了 OP 的迫切需求。有时您需要处理所掌握的信息。
【解决方案2】:

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

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 );
# ...

【讨论】:

  • 如果对象 Config::IniFile 接受对标量的引用,则配置文件的每个“段落”都可以单独解析,从而完成工作。
【解决方案3】:

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

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",
         },
}

【讨论】:

  • @MUYBelgium:我完全不明白你的评论,尤其是你对引号的使用。很难辨别你是在寻求智慧,还是只是希望为你的言辞提供更多燃料。如果您查看perldoc perlvar,您会发现每个短名称都有一个原因和一个助记符。我希望你能理解像substr($str, $-[0], $+[0]-$-[0]) 这样的成语比像COBOL 的substr($str, $LAST_MATCH_START[0], $LAST_MATCH_END[0] - $LAST_MATCH_START[0]) 更可取?
  • @MUYBelgium:嗯。管理员似乎也对您的态度持暗淡的态度。当您在这里发帖时,请仔细考虑您的举止和风格。 Stack Overflow 的绝大多数贡献者都以他们的帖子为荣,不恰当地涉入自负和不屑一顾的反驳。
【解决方案4】:

您可能希望对一系列对象(如 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 )  
} 

和其他答案一样。

【讨论】:

    猜你喜欢
    • 2012-10-09
    • 1970-01-01
    • 2012-03-29
    • 2011-02-22
    • 2021-05-14
    • 2013-04-18
    • 2015-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多