使用 Perl 将 CSV 文件转换为 XML
Converting CSV file to XML with Perl
我正在尝试解析 CSV 文件并将其转换为 XML。 .csv 文件由一个条目列表组成,条目之间以逗号分隔。因此,两个示例条目如下所示:
License,Date,Mileage
04-nh-pd,17-11-2020,30000
19-tg-jr,17-11-2020,36000
预期输出:
<?xml version="1.0" encoding="UTF-8" ?><ns1:ImportObjectMileage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types">
<ns1:ObjectMileage><ns1:object_code>04-nh-pd</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>30000</ns1:mileage><ns1:icode_mileagecause_ecode>KEUR</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code>19-tg-jr</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>36000</ns1:mileage><ns1:icode_mileagecause_ecode>KEUR</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
到目前为止我的代码:
#!perl
use strict;
# Open the ch2_xml_users.csv file for input
open(CSV_FILE, "ch2_xmlusers.csv") || die "Can't open file: $!";
# Open the ch2_xmlusers.xml file for output
open(XML_FILE, ">ch2_xmlusers.xml") || die "Can't open file: $!";
# Print the initial XML header and the root element
print XML_FILE '<?xml version="1.0" encoding="UTF-8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types';
my $kenteken = "";
# The while loop to traverse through each line in users.csv
while(<CSV_FILE>) {
chomp; # Delete the new line char for each line
# Split each field, on the comma delimiter, into an array
my @fields = split(/,/, $_);
$kenteken .= <<"EOF";
<ns1:ObjectMileage><ns1:object_code>$fields[0]</ns1:object_code><ns1:mileagedate>$fields[1]</ns1:mileagedate><ns1:mileage>$fields[2]</ns1:mileage><ns1:icode_mileagecause_ecode>$fields[3]</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
EOF
}
print XML_FILE "\n".$kenteken."\n";
# Close all open files
close CSV_FILE;
close XML_FILE;
到目前为止我的输出:
<?xml version="1.0" encoding="UTF-8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types
<ns1:ObjectMileage><ns1:object_code>License</ns1:object_code><ns1:mileagedate>Date</ns1:mileagedate><ns1:mileage>Mileage</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code>04-nh-pd</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>30000</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code>19-tg-jr</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>36000</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code></ns1:object_code><ns1:mileagedate></ns1:mileagedate><ns1:mileage></ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code></ns1:object_code><ns1:mileagedate></ns1:mileagedate><ns1:mileage></ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
header 下面的第一行和最后两行不应显示在输出中。
数据之间的空行也不正确。有人可以帮我写剧本吗?
您在 heredoc 中添加了 2 个换行符,打印时又添加了 2 个。如果您不想要那么多换行符,为什么不删除其中的一些呢?
至于你的输出,你可能会考虑在循环内声明变量,然后直接打印:
while (<>) {
...
my $kenteken = ....
print ...
}
这样每一行新的输入都会得到一个新的临时变量。
但是,既然可以跳过它,为什么还要使用临时变量呢?例如,您可以像这样使用 printf
:
printf XML_FILE "<ns1:ObjectMileage><ns1:object_code>%s</ns1:object_code><ns1:mileagedate>%s</ns1:mileagedate><ns1:mileage>%s</ns1:mileage><ns1:icode_mileagecause_ecode>%s</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>\n", @fields;
用法是 printf "%s", $var
,其中 %s
表示 $var
提供的字符串的占位符。请注意,我在末尾添加了换行符 \n
,这通常是打印一行的方式。
末尾没有值的两行可能是输入文件中的空行。如果您在代码中使用了 use warnings
,您就会知道这一点。因为你没有,所以你没有收到关于输入中空行的警告,它看起来像这样:
Use of uninitialized value in concatenation (.) or string at ...
您可以检查输入文件行并跳过空行来避免这种情况。例如:
while (<>) {
next unless /\S/; # skip lines without non-whitespace characters
那么……说了这么多,这不是你应该做的。您应该(可能)使用 csv 模块,例如 Text::CSV
to read your input file, and then use an xml-module to print it. I am not terribly familiar with these, but if you google, you should find some recommendations. I have heard some recommend XML::LibXML
。不过,不要问关于模块的建议问题,因为这不是 Whosebug 的主题。如评论中所述,像您所做的那样打印简单的 XML 可能没问题。
我已对您的脚本进行了以下更改,看看是否适合您。
- 始终使用词法文件句柄进行文件操作。
- xml header 行结束于
..types">
- 有几种方法可以跳过 CSV 文件的 header:
3.1 通过将一行读入循环上方的空上下文来摆脱 header 的模式匹配(如评论中提到的@simbabque)。
3.2 如果CSV文件line
匹配(=~
)和License,Date,Mileage
,则跳过next
语句的行。
- 不是一个接一个地连接
kentekens
,而是在 csv 读取操作本身时用必填字段写入行内容。
下面是修改后的脚本:
use strict; use warnings;
no warnings 'uninitialized';
open my $CSV_FILE, "<", "ch2_xmlusers.csv" or die "Cannot open a file: $!";
open my $XML_FILE, ">", "ch2_xmlusers.xml" or die "Cannot open a file: $!";
print $XML_FILE '<?xml version="1.0" encoding="UTF-8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types">'."\n";
my $kenteken = "";
my $csv_header = <$CSV_FILE>;
while(<$CSV_FILE>) {
chomp;
my @fields = split ',', $_;
$kenteken = <<"EOF";
<ns1:ObjectMileage><ns1:object_code>$fields[0]</ns1:object_code><ns1:mileagedate>$fields[1]</ns1:mileagedate><ns1:mileage>$fields[2]</ns1:mileage><ns1:icode_mileagecause_ecode>$fields[3]</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
EOF
print $XML_FILE $kenteken;
}
close $CSV_FILE;
close $XML_FILE;
结果:
<?xml version="1.0" encoding="UTF-8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types">
<ns1:ObjectMileage><ns1:object_code>04-nh-pd</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>30000
</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code>19-tg-jr</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>36000</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
我正在尝试解析 CSV 文件并将其转换为 XML。 .csv 文件由一个条目列表组成,条目之间以逗号分隔。因此,两个示例条目如下所示:
License,Date,Mileage
04-nh-pd,17-11-2020,30000
19-tg-jr,17-11-2020,36000
预期输出:
<?xml version="1.0" encoding="UTF-8" ?><ns1:ImportObjectMileage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types">
<ns1:ObjectMileage><ns1:object_code>04-nh-pd</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>30000</ns1:mileage><ns1:icode_mileagecause_ecode>KEUR</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code>19-tg-jr</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>36000</ns1:mileage><ns1:icode_mileagecause_ecode>KEUR</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
到目前为止我的代码:
#!perl
use strict;
# Open the ch2_xml_users.csv file for input
open(CSV_FILE, "ch2_xmlusers.csv") || die "Can't open file: $!";
# Open the ch2_xmlusers.xml file for output
open(XML_FILE, ">ch2_xmlusers.xml") || die "Can't open file: $!";
# Print the initial XML header and the root element
print XML_FILE '<?xml version="1.0" encoding="UTF-8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types';
my $kenteken = "";
# The while loop to traverse through each line in users.csv
while(<CSV_FILE>) {
chomp; # Delete the new line char for each line
# Split each field, on the comma delimiter, into an array
my @fields = split(/,/, $_);
$kenteken .= <<"EOF";
<ns1:ObjectMileage><ns1:object_code>$fields[0]</ns1:object_code><ns1:mileagedate>$fields[1]</ns1:mileagedate><ns1:mileage>$fields[2]</ns1:mileage><ns1:icode_mileagecause_ecode>$fields[3]</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
EOF
}
print XML_FILE "\n".$kenteken."\n";
# Close all open files
close CSV_FILE;
close XML_FILE;
到目前为止我的输出:
<?xml version="1.0" encoding="UTF-8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types
<ns1:ObjectMileage><ns1:object_code>License</ns1:object_code><ns1:mileagedate>Date</ns1:mileagedate><ns1:mileage>Mileage</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code>04-nh-pd</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>30000</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code>19-tg-jr</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>36000</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code></ns1:object_code><ns1:mileagedate></ns1:mileagedate><ns1:mileage></ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code></ns1:object_code><ns1:mileagedate></ns1:mileagedate><ns1:mileage></ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
header 下面的第一行和最后两行不应显示在输出中。 数据之间的空行也不正确。有人可以帮我写剧本吗?
您在 heredoc 中添加了 2 个换行符,打印时又添加了 2 个。如果您不想要那么多换行符,为什么不删除其中的一些呢?
至于你的输出,你可能会考虑在循环内声明变量,然后直接打印:
while (<>) {
...
my $kenteken = ....
print ...
}
这样每一行新的输入都会得到一个新的临时变量。
但是,既然可以跳过它,为什么还要使用临时变量呢?例如,您可以像这样使用 printf
:
printf XML_FILE "<ns1:ObjectMileage><ns1:object_code>%s</ns1:object_code><ns1:mileagedate>%s</ns1:mileagedate><ns1:mileage>%s</ns1:mileage><ns1:icode_mileagecause_ecode>%s</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>\n", @fields;
用法是 printf "%s", $var
,其中 %s
表示 $var
提供的字符串的占位符。请注意,我在末尾添加了换行符 \n
,这通常是打印一行的方式。
末尾没有值的两行可能是输入文件中的空行。如果您在代码中使用了 use warnings
,您就会知道这一点。因为你没有,所以你没有收到关于输入中空行的警告,它看起来像这样:
Use of uninitialized value in concatenation (.) or string at ...
您可以检查输入文件行并跳过空行来避免这种情况。例如:
while (<>) {
next unless /\S/; # skip lines without non-whitespace characters
那么……说了这么多,这不是你应该做的。您应该(可能)使用 csv 模块,例如 Text::CSV
to read your input file, and then use an xml-module to print it. I am not terribly familiar with these, but if you google, you should find some recommendations. I have heard some recommend XML::LibXML
。不过,不要问关于模块的建议问题,因为这不是 Whosebug 的主题。如评论中所述,像您所做的那样打印简单的 XML 可能没问题。
我已对您的脚本进行了以下更改,看看是否适合您。
- 始终使用词法文件句柄进行文件操作。
- xml header 行结束于
..types">
- 有几种方法可以跳过 CSV 文件的 header:
3.1 通过将一行读入循环上方的空上下文来摆脱 header 的模式匹配(如评论中提到的@simbabque)。
3.2 如果CSV文件line
匹配(=~
)和License,Date,Mileage
,则跳过next
语句的行。 - 不是一个接一个地连接
kentekens
,而是在 csv 读取操作本身时用必填字段写入行内容。
下面是修改后的脚本:
use strict; use warnings;
no warnings 'uninitialized';
open my $CSV_FILE, "<", "ch2_xmlusers.csv" or die "Cannot open a file: $!";
open my $XML_FILE, ">", "ch2_xmlusers.xml" or die "Cannot open a file: $!";
print $XML_FILE '<?xml version="1.0" encoding="UTF-8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types">'."\n";
my $kenteken = "";
my $csv_header = <$CSV_FILE>;
while(<$CSV_FILE>) {
chomp;
my @fields = split ',', $_;
$kenteken = <<"EOF";
<ns1:ObjectMileage><ns1:object_code>$fields[0]</ns1:object_code><ns1:mileagedate>$fields[1]</ns1:mileagedate><ns1:mileage>$fields[2]</ns1:mileage><ns1:icode_mileagecause_ecode>$fields[3]</ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
EOF
print $XML_FILE $kenteken;
}
close $CSV_FILE;
close $XML_FILE;
结果:
<?xml version="1.0" encoding="UTF-8" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns1="http://www.co-maker.nl/LeaseOffice/types">
<ns1:ObjectMileage><ns1:object_code>04-nh-pd</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>30000
</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>
<ns1:ObjectMileage><ns1:object_code>19-tg-jr</ns1:object_code><ns1:mileagedate>17-11-2020</ns1:mileagedate><ns1:mileage>36000</ns1:mileage><ns1:icode_mileagecause_ecode></ns1:icode_mileagecause_ecode></ns1:ObjectMileage>