使用 Perl 将 CSV 文件转换为 XML

Converting CSV file to XML with Perl

提问人:Toine de L 提问时间:1/25/2021 最后编辑:galizienToine de L 更新时间:1/26/2021 访问量:269

问:

我正在尝试解析 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 行不应显示在输出中。 此外,数据之间的空行不正确。有人可以帮我写剧本吗?

Perl XML 解析

评论

0赞 simbabque 1/26/2021
您不是缺少 的结束 XML 标记吗?<ns1:ImportObjectMileage>

答:

3赞 TLP 1/26/2021 #1

您在 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 可能会很好。

评论

0赞 simbabque 1/26/2021
我不认为XML在这里只是文本有问题。他们毕竟没有读它。这是一个固定的结构,所以这是完全安全的。不过,在一个更大的系统中,我可能会有一个特定文件的模板。
1赞 vkk05 1/26/2021 #2

我对你的脚本进行了以下更改,看看这是否适合你。

  1. 始终使用词法文件句柄进行文件操作。
  2. xml 标题行关闭方式..types">
  3. 有几种方法可以跳过 CSV 文件的标头:
    3.1 通过将一行读入循环上方的 void 上下文来摆脱标头的模式匹配(如评论中提到的@simbabque)。
    3.2 如果 CSV 文件与 () 匹配,则跳过 with 语句行。
    line=~License,Date,Mileagenext
  4. 而不是一个接一个地连接,而是在 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>   

评论

0赞 simbabque 1/26/2021
您可以通过将一行读入循环上方的 void 上下文来摆脱标头的模式匹配:<$CSV_FILE>; while ...
0赞 vkk05 1/26/2021
@simbabque:是的,这是个好主意。更新了答案。