提问人:Toine de L 提问时间:1/25/2021 最后编辑:galizienToine de L 更新时间:1/26/2021 访问量:269
使用 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>
标题下方的第一行和最后 2 行不应显示在输出中。 此外,数据之间的空行不正确。有人可以帮我写剧本吗?
答:
您在 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
)来读取输入文件,然后使用 xml 模块来打印它。我对这些不是很熟悉,但如果你用谷歌搜索,你应该会找到一些建议。我听说过一些推荐XML::LibXML
的人。但是,不要问一个问题来寻求有关模块的建议,因为这与 stackoverflow 无关。如注释中所述,像您所做的那样打印简单的 XML 可能会很好。
评论
我对你的脚本进行了以下更改,看看这是否适合你。
- 始终使用词法文件句柄进行文件操作。
- xml 标题行关闭方式
..types">
- 有几种方法可以跳过 CSV 文件的标头:
3.1 通过将一行读入循环上方的 void 上下文来摆脱标头的模式匹配(如评论中提到的@simbabque)。
3.2 如果 CSV 文件与 () 匹配,则跳过 with 语句行。line
=~
License,Date,Mileage
next
- 而不是一个接一个地连接,而是在 csv 读取操作本身时用必填字段写入行内容。
kentekens
以下是更改后的脚本:
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_FILE>; while ...
评论
<ns1:ImportObjectMileage>