【问题标题】:Converting CSV file to XML with Perl使用 Perl 将 CSV 文件转换为 XML
【发布时间】:2021-04-29 10:24:47
【问题描述】:

我正在尝试解析 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>
    


标题下方的第一行和最后两行不应显示在输出中。 数据之间的空行也不正确。有人可以帮我写剧本吗?

【问题讨论】:

  • 你是不是错过了&lt;ns1:ImportObjectMileage&gt; 的结束 XML 标记?

标签: perl xml-parsing


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

那么现在....说了这么多,但您不应该这样做。您应该(可能)使用诸如 Text::CSV 之类的 csv 模块来读取您的输入文件,然后使用 xml 模块来打印它。我对这些不是很熟悉,但如果你用谷歌搜索,你应该会找到一些建议。我听说有人推荐XML::LibXML。但是,不要问有关模块建议的问题,因为这与 stackoverflow 无关。正如 cmets 中所指出的,可以像您所做的那样打印简单的 XML。

【讨论】:

  • 我认为 XML 在这里只是文本没有问题。毕竟他们不是在读它。这是一个固定的结构,所以这是完全安全的。不过,在更大的系统中,我可能会有一个用于该特定文件的模板。
【解决方案2】:

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

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

【讨论】:

  • 您可以通过将一行读入循环上方的 void 上下文来摆脱标题的模式匹配:&lt;$CSV_FILE&gt;; while ...
  • @simbabque:是的,这是个好主意。更新了答案。
猜你喜欢
  • 1970-01-01
  • 2018-01-23
  • 2011-12-25
  • 2019-12-15
  • 2021-05-08
  • 2021-11-21
  • 1970-01-01
  • 2014-02-20
  • 2016-07-21
相关资源
最近更新 更多