将复杂的配置文件转换为 id,parent,key,value 格式
Convert complex config file to id,parent,key,value format
我正在尝试像下面的示例那样转换配置文件,这样我就可以生成 SQL 命令以插入到我的 Oracle table 中。我正在尝试使用 Perl,但我愿意尝试其他语言,例如 Python.
配置示例:
# This is a comment
feed_realtime_processor_pool = ( 11, 12 ) ;
dropout_detection_time_start = "17:00";
# Sometimes the config can have sub-structures
named_clients = (
{
name = "thread1";
user_threads = (
{ name = "realtime1"; cpu = 11; } # more comments
{ name = "realtime2"; cpu = 12; } # more comments
);
}
);
(...)
正在转换(空格只是为了更好的可视化):
id,parent, key, value
01,null, 'feed_realtime_processor_pool', '11'
02,null, 'feed_realtime_processor_pool', '12'
03,null, 'dropout_detection_time_start', '17:00'
04,null, 'named_clients', null
05,04, 'name', 'thread1'
06,04, 'user_threads', null
07,06, 'name', 'realtime1'
08,06, 'cpu', '11'
09,06, 'name', 'realtime2'
10,06, 'cpu', '12'
所以,问题是:
有人知道一些 lib/module 可以做到这一点吗?我可以找到配置解析器,但找不到可以给我提供树格式的好配置解析器。
如果没有,有人可以给我一些关于我应该如何启动这个脚本的建议,也许使用不同的模块来帮助我解析配置?清理和解析配置对我来说是最难的部分。
第一次更新:
- 我忘了说,我有大量配置要导入到我的数据库中,这不是一次性事件。每当我公司的客户为不同的安装生成新配置时,此脚本都会 运行。
- 我选择 perl 是因为我认为它的执行速度可能比 Python 快。
- 我很熟悉SQL,但我不太擅长Perl
- 配置遵循我在示例中放置的相同结构,唯一的变化与 key/values 对之间的空格数或花括号的缩进等有关
- 我已经开始使用 DBI 编写 perl 脚本,但主要问题是将此配置解析为我可以轻松使用的格式。我总是发现我的正则表达式被破坏的情况,并且我不断地调整。如果我可以只使用一个库来自动解析配置,那就太好了。我要试试你们提到的那些。
谢谢!
第二次更新:
伙计们,我在 Perl Monks 上交叉 post 编辑了这个问题,并在那里得到了另一个反馈,我也在测试。一旦我测试了所有内容,post 会给出答案吗?谢谢
听起来你希望 Perl 分解配置文件并从中构建 SQL 语句,你可以这样做,但这样做也意味着创建 ID,如果数据库自动递增。
我建议学习如何使用 Perl 直接与数据库交互。该区域由两个模块控制:DBI and DBIx::Class.
这两个模块都提供了 create/read/update/delete 数据的方法,并且可以存储来自查询的 return 值,因此您可以在后续查询中使用 ID 和其他 table 数据。他们还对事务进行错误处理,这总是很好。
如果这是一次性脚本并且您已经熟悉 SQL,我推荐 DBI,因为它使用了很多普通的 SQL 并且可能会更快地学习.
如果您想要在 Perl 中使用数据库的 OO 解决方案,或者如果您打算进行大量数据库工作,那么我建议您花时间学习 DBIx::Class,因为您可以创建一个完整的更强大解决方案的架构模型。
无论哪种情况,您还需要 DBD::Oracle 模块,其中包含规则 DBI 和 DBIx::Class 需要与 Oracle 数据库交互。
更新:
我忘了提...如果您坚持将配置文件转换为 SQL 语句,您可以尝试 SQL::Abstract 将 Perl 数据结构转换为 SQL声明。
更新2:
我不确定是否有针对该特定配置语法的模块,但它看起来非常接近 JSON 的格式,因此您可以将文件转换为该格式,然后使用 JSON.
需要删除注释,数组从括号更改为方括号,等号更改为冒号,所有文本都需要用引号引起来,分号更改为逗号。我认为最棘手的部分是从每个块中删除最后一个逗号。
在删除尾随逗号时遇到困难,但这应该可以帮助您完成大部分工作:
my $config = <<'CONFIG';
# This is a comment
feed_realtime_processor_pool = ( 11, 12 ) ;
dropout_detection_time_start = "17:00";
# Sometimes the config can have sub-structures
named_clients = (
{
name = "thread1";
user_threads = (
{ name = "realtime1"; cpu = 11; } # more comments
{ name = "realtime2"; cpu = 12; } # more comments
);
}
);
CONFIG
# Remove comments
$config =~ s{[#].+?$}{}mg;
# Convert Arrays
$config =~ s{\(}{[}mg;
$config =~ s{\)}{]}mg;
# Convert key-value seaprators
$config =~ s{=}{:}mg;
# Wrap text in quotes
$config =~ s{"?([\w\d]+)"?}{""}mg;
$config =~ s{"(\d\d?)":"(\d\d?)"}{":"}mg; # fix times
# Convert eol delimiters
$config =~ s{;}{,}mg;
# Wrap whole thing in brackets
$config = '{' . $config . '}';
print "$config\n\n";
撇开 Oracle,您也许可以对 Python 中的 "config" 文件做一些事情,因为它的语法即使不兼容也很相似(我不能告诉你)。但是我不知道会在 Perl 中对其进行评估的东西。相反,您可以尝试 Config::General 这将允许一个如下所示的配置文件:
feed_realtime_processor_pool 11
feed_realtime_processor_pool 12
dropout_detection_time_start 17:00
# Sometimes the config can have sub-structures
<named_client thread1>
<user_threads realtime1>
cpu 11
</user_threads>
<user_threads realtime2>
cpu 12
</user_threads>
</named_client>
# etc
然后阅读:
use Config::General;
$config = Config::General->new("my.conf");
my %config = $config->getall;
更简单的替代方案可能是 JSON 和 use JSON
或 XML::Simple 等。在 CPAN 中查看它们。这些将为您提供熟悉语法的树格式。
感谢大家的提示,但我认为最好的选择是遵循我在 perlmonks 站点上从 Choroba 收到的建议。我在这里复制他的回答:
“如果找不到解析配置格式的模块,请编写自己的解析器。Marpa::R2 可以帮助您完成任务:
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
use Marpa::R2;
my $input = << '__INPUT__';
# This is a comment
feed_realtime_processor_pool = ( 11, 12 ) ;
dropout_detection_time_start = "17:00";
# Sometimes the config can have sub-structures
named_clients = (
{
name = "thread1";
user_threads = (
{ name = "realtime1"; cpu = 11; } # more comments
{ name = "realtime2"; cpu = 12; } # more comments
);
}
);
__INPUT__
my $dsl = << '__DSL__';
lexeme default = latm => 1
:default ::= action => ::first
Config ::= Elements
Elements ::= Element+ action => grep_def
+ined
Element ::= (Comment) action => empty
| Name (s eq s) Value action => [values]
| Name (s eq s) Value (semicolon s) action => [values]
Comment ::= (hash nonnl nl) action => empty
Name ::= alpha
Value ::= List
| String
| Num
| Struct
List ::= (lpar) Nums (rpar s semicolon s)
Nums ::= Num+ separator => comma action => listify
Num ::= (s) digits (s)
| (s) digits
| digits (s)
| digits
String ::= (qq) nqq (qq semicolon s) action => quote
Struct ::= (lpar s) InStructs (rpar semicolon s)
InStructs ::= InStruct+ action => grep_def
+ined
InStruct ::= (lcurl s) Elements (rcurl s)
| (Comment s) action => empty
| Element
s ~ [\s]*
eq ~ '='
hash ~ '#'
nonnl ~ [^\n]*
nl ~ [\n]
alpha ~ [a-z_]+
lpar ~ '('
rpar ~ ')'
lcurl ~ '{'
rcurl ~ '}'
semicolon ~ ';'
comma ~ ','
digits ~ [\d]+
qq ~ '"'
nqq ~ [^"]+
__DSL__
sub listify { shift; [ @_ ] }
sub quote { qq("$_[1]") }
sub empty {}
sub grep_defined { shift; [ grep defined, @_ ] }
my $id = 1;
sub show {
my ($parent, $name, $elems) = @_;
if (ref $elems->[0]) {
show($parent, $name, $_) for @$elems;
} elsif (ref $elems->[1]) {
if (ref $elems->[1][0]) {
say join ', ', $id, $parent, $elems->[0], 'null';
show($id++, $elems->[0], $elems->[1]);
} else {
for my $e (@{ $elems->[1] }) {
say join ', ', $id++, $parent, $elems->[0], $e;
}
}
} else {
say join ', ', $id++, $parent, @$elems;
}
}
my $grammar = 'Marpa::R2::Scanless::G'->new({ source => $dsl });
show('null', q(), ${ $grammar->parse($input, 'main') });
[download]
Output:
1, null, feed_realtime_processor_pool, 11
2, null, feed_realtime_processor_pool, 12
3, null, dropout_detection_time_start, "17:00"
4, null, named_clients, null
5, 4, name, "thread1"
6, 4, user_threads, null
7, 6, name, "realtime1"
8, 6, cpu, 11
9, 6, name, "realtime2"
10, 6, cpu, 12
请注意,要安装此模块,我们需要安装许多依赖项:
sudo cpan IPC::Cmd
sudo cpan Module::Build
sudo cpan Time::Piece
sudo cpan Marpa::R2
我正在尝试像下面的示例那样转换配置文件,这样我就可以生成 SQL 命令以插入到我的 Oracle table 中。我正在尝试使用 Perl,但我愿意尝试其他语言,例如 Python.
配置示例:
# This is a comment
feed_realtime_processor_pool = ( 11, 12 ) ;
dropout_detection_time_start = "17:00";
# Sometimes the config can have sub-structures
named_clients = (
{
name = "thread1";
user_threads = (
{ name = "realtime1"; cpu = 11; } # more comments
{ name = "realtime2"; cpu = 12; } # more comments
);
}
);
(...)
正在转换(空格只是为了更好的可视化):
id,parent, key, value
01,null, 'feed_realtime_processor_pool', '11'
02,null, 'feed_realtime_processor_pool', '12'
03,null, 'dropout_detection_time_start', '17:00'
04,null, 'named_clients', null
05,04, 'name', 'thread1'
06,04, 'user_threads', null
07,06, 'name', 'realtime1'
08,06, 'cpu', '11'
09,06, 'name', 'realtime2'
10,06, 'cpu', '12'
所以,问题是:
有人知道一些 lib/module 可以做到这一点吗?我可以找到配置解析器,但找不到可以给我提供树格式的好配置解析器。
如果没有,有人可以给我一些关于我应该如何启动这个脚本的建议,也许使用不同的模块来帮助我解析配置?清理和解析配置对我来说是最难的部分。
第一次更新:
- 我忘了说,我有大量配置要导入到我的数据库中,这不是一次性事件。每当我公司的客户为不同的安装生成新配置时,此脚本都会 运行。
- 我选择 perl 是因为我认为它的执行速度可能比 Python 快。
- 我很熟悉SQL,但我不太擅长Perl
- 配置遵循我在示例中放置的相同结构,唯一的变化与 key/values 对之间的空格数或花括号的缩进等有关
- 我已经开始使用 DBI 编写 perl 脚本,但主要问题是将此配置解析为我可以轻松使用的格式。我总是发现我的正则表达式被破坏的情况,并且我不断地调整。如果我可以只使用一个库来自动解析配置,那就太好了。我要试试你们提到的那些。
谢谢!
第二次更新:
伙计们,我在 Perl Monks 上交叉 post 编辑了这个问题,并在那里得到了另一个反馈,我也在测试。一旦我测试了所有内容,post 会给出答案吗?谢谢
听起来你希望 Perl 分解配置文件并从中构建 SQL 语句,你可以这样做,但这样做也意味着创建 ID,如果数据库自动递增。
我建议学习如何使用 Perl 直接与数据库交互。该区域由两个模块控制:DBI and DBIx::Class.
这两个模块都提供了 create/read/update/delete 数据的方法,并且可以存储来自查询的 return 值,因此您可以在后续查询中使用 ID 和其他 table 数据。他们还对事务进行错误处理,这总是很好。
如果这是一次性脚本并且您已经熟悉 SQL,我推荐 DBI,因为它使用了很多普通的 SQL 并且可能会更快地学习.
如果您想要在 Perl 中使用数据库的 OO 解决方案,或者如果您打算进行大量数据库工作,那么我建议您花时间学习 DBIx::Class,因为您可以创建一个完整的更强大解决方案的架构模型。
无论哪种情况,您还需要 DBD::Oracle 模块,其中包含规则 DBI 和 DBIx::Class 需要与 Oracle 数据库交互。
更新:
我忘了提...如果您坚持将配置文件转换为 SQL 语句,您可以尝试 SQL::Abstract 将 Perl 数据结构转换为 SQL声明。
更新2:
我不确定是否有针对该特定配置语法的模块,但它看起来非常接近 JSON 的格式,因此您可以将文件转换为该格式,然后使用 JSON.
需要删除注释,数组从括号更改为方括号,等号更改为冒号,所有文本都需要用引号引起来,分号更改为逗号。我认为最棘手的部分是从每个块中删除最后一个逗号。
在删除尾随逗号时遇到困难,但这应该可以帮助您完成大部分工作:
my $config = <<'CONFIG';
# This is a comment
feed_realtime_processor_pool = ( 11, 12 ) ;
dropout_detection_time_start = "17:00";
# Sometimes the config can have sub-structures
named_clients = (
{
name = "thread1";
user_threads = (
{ name = "realtime1"; cpu = 11; } # more comments
{ name = "realtime2"; cpu = 12; } # more comments
);
}
);
CONFIG
# Remove comments
$config =~ s{[#].+?$}{}mg;
# Convert Arrays
$config =~ s{\(}{[}mg;
$config =~ s{\)}{]}mg;
# Convert key-value seaprators
$config =~ s{=}{:}mg;
# Wrap text in quotes
$config =~ s{"?([\w\d]+)"?}{""}mg;
$config =~ s{"(\d\d?)":"(\d\d?)"}{":"}mg; # fix times
# Convert eol delimiters
$config =~ s{;}{,}mg;
# Wrap whole thing in brackets
$config = '{' . $config . '}';
print "$config\n\n";
撇开 Oracle,您也许可以对 Python 中的 "config" 文件做一些事情,因为它的语法即使不兼容也很相似(我不能告诉你)。但是我不知道会在 Perl 中对其进行评估的东西。相反,您可以尝试 Config::General 这将允许一个如下所示的配置文件:
feed_realtime_processor_pool 11
feed_realtime_processor_pool 12
dropout_detection_time_start 17:00
# Sometimes the config can have sub-structures
<named_client thread1>
<user_threads realtime1>
cpu 11
</user_threads>
<user_threads realtime2>
cpu 12
</user_threads>
</named_client>
# etc
然后阅读:
use Config::General;
$config = Config::General->new("my.conf");
my %config = $config->getall;
更简单的替代方案可能是 JSON 和 use JSON
或 XML::Simple 等。在 CPAN 中查看它们。这些将为您提供熟悉语法的树格式。
感谢大家的提示,但我认为最好的选择是遵循我在 perlmonks 站点上从 Choroba 收到的建议。我在这里复制他的回答:
“如果找不到解析配置格式的模块,请编写自己的解析器。Marpa::R2 可以帮助您完成任务:
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
use Marpa::R2;
my $input = << '__INPUT__';
# This is a comment
feed_realtime_processor_pool = ( 11, 12 ) ;
dropout_detection_time_start = "17:00";
# Sometimes the config can have sub-structures
named_clients = (
{
name = "thread1";
user_threads = (
{ name = "realtime1"; cpu = 11; } # more comments
{ name = "realtime2"; cpu = 12; } # more comments
);
}
);
__INPUT__
my $dsl = << '__DSL__';
lexeme default = latm => 1
:default ::= action => ::first
Config ::= Elements
Elements ::= Element+ action => grep_def
+ined
Element ::= (Comment) action => empty
| Name (s eq s) Value action => [values]
| Name (s eq s) Value (semicolon s) action => [values]
Comment ::= (hash nonnl nl) action => empty
Name ::= alpha
Value ::= List
| String
| Num
| Struct
List ::= (lpar) Nums (rpar s semicolon s)
Nums ::= Num+ separator => comma action => listify
Num ::= (s) digits (s)
| (s) digits
| digits (s)
| digits
String ::= (qq) nqq (qq semicolon s) action => quote
Struct ::= (lpar s) InStructs (rpar semicolon s)
InStructs ::= InStruct+ action => grep_def
+ined
InStruct ::= (lcurl s) Elements (rcurl s)
| (Comment s) action => empty
| Element
s ~ [\s]*
eq ~ '='
hash ~ '#'
nonnl ~ [^\n]*
nl ~ [\n]
alpha ~ [a-z_]+
lpar ~ '('
rpar ~ ')'
lcurl ~ '{'
rcurl ~ '}'
semicolon ~ ';'
comma ~ ','
digits ~ [\d]+
qq ~ '"'
nqq ~ [^"]+
__DSL__
sub listify { shift; [ @_ ] }
sub quote { qq("$_[1]") }
sub empty {}
sub grep_defined { shift; [ grep defined, @_ ] }
my $id = 1;
sub show {
my ($parent, $name, $elems) = @_;
if (ref $elems->[0]) {
show($parent, $name, $_) for @$elems;
} elsif (ref $elems->[1]) {
if (ref $elems->[1][0]) {
say join ', ', $id, $parent, $elems->[0], 'null';
show($id++, $elems->[0], $elems->[1]);
} else {
for my $e (@{ $elems->[1] }) {
say join ', ', $id++, $parent, $elems->[0], $e;
}
}
} else {
say join ', ', $id++, $parent, @$elems;
}
}
my $grammar = 'Marpa::R2::Scanless::G'->new({ source => $dsl });
show('null', q(), ${ $grammar->parse($input, 'main') });
[download]
Output:
1, null, feed_realtime_processor_pool, 11
2, null, feed_realtime_processor_pool, 12
3, null, dropout_detection_time_start, "17:00"
4, null, named_clients, null
5, 4, name, "thread1"
6, 4, user_threads, null
7, 6, name, "realtime1"
8, 6, cpu, 11
9, 6, name, "realtime2"
10, 6, cpu, 12
请注意,要安装此模块,我们需要安装许多依赖项:
sudo cpan IPC::Cmd
sudo cpan Module::Build
sudo cpan Time::Piece
sudo cpan Marpa::R2