用于将Csv转换为xml的脚本

时间:2018-06-18 06:46:44

标签: linux bash awk scripting

需要使用for循环处理文件

我写了下面的代码将csv转换为xml。这里为每一列写了单独的标签 在输入文件中有1到278列。在输出文件中需要有从A1到A278的标记,

代码:

file_in="Prepaid_plan_voucher.csv"
file_out="Prepaid_plan_voucher.xml"
echo '<?xml version="1.0"?>' > $file_out
#echo '<Customers>' >> $file_out
echo '  <TariffRecords>' >> $file_out
echo '  <Tariff>' >> $file_out
while IFS=$',' read -r -a arry
do
#  echo '  <TariffRecords>' >> $file_out
#  echo '  <Tariff>' >> $file_out
  echo '    <A1>'${arry[0]}'</A1>' >> $file_out
  echo '    <A2>'${arry[1]}'</A2>' >> $file_out
  echo '    <A3>'${arry[2]}'</A3>' >> $file_out
#  echo '  </TariffRecords>' >> $file_out
#  echo '  </Tariff>' >> $file_out
done < $file_in
#echo '</Customers>' >> $file_out
echo '  <TariffRecords>' >> $file_out
echo '  <Tariff>' >> $file_out

示例输入文件。(这是实际输入文件中的示例记录,将包含278列)。 如果输入文件有两个或三个记录,则需要在一个XML文件中附加相同的记录。

name,Tariff Summary,Record ID No.,Operator Name,Circle (Service Area),list
Prepaid Plan Voucher,test_All calls 2p/s,TT07PMPV0188,Ta Te,Gu,
Prepaid Plan Voucher,test_All calls 3p/s,TT07PMPV0189,Ta Te,HR,

示例输出文件      以上两个关税记录,关税将在xml文件的开头和结尾进行硬编码。

<TariffRecords>
<Tariff>
<A1>Prepaid Plan Voucher</A1>
<A2>test_All calls 2p/s</A2>
<A3>TT07PMPV0188</A3>
<A4>Ta Te</A4>
<A5>Gu</A5>
<A6></A6>
<Tariff>
<Tariff>
<A1>Prepaid Plan Voucher</A1>
<A2>test_All calls 3p/s</A2>
<A3>TT07PMPV0189</A3>
<A4>Ta Te</A4>
<A5>HR</A5>
<A6></A6>
<Tariff>
<TariffRecords>

2 个答案:

答案 0 :(得分:2)

尽管如此,这不是最优雅的解决方案,但我认为如果我理解正确的话,你只想这样做。因此,尽可能多地修改代码:

NUM_OF_COLS=5
echo '<TariffRecords>' >> $file_out
while IFS=$',' read -r -a arry
do
  tariff="  <Tariff>\n"
  for i in $(seq 0 $NUM_OF_COLS); do
    tariff="${tariff}    <A$i>${arry[$i]}</A$i>\n"
  done
  tariff="${tariff}  </Tariff>"
  echo -e ${tariff} >> $file_out
done < <(tail -n +1 $file_in)
echo '</TariffRecords>' >> $file_out

需要注意的事项:

我们正在通过以下方式跳过CSV标题:

<(tail -n +1 $file_in)

0$NUM_OF_COLS范围内生成“foeach”循环,代表列的索引:

$(seq 0 $NUM_OF_COLS)

追加字符串:

tariff="${tariff}......"

使用

echo -e ...

为了保留新的行和良好的格式,但您可以使用其他bash实用程序,例如xmllint,以便进行相当格式化。

编辑:适用于多个文件

要处理多个文件,请替换硬编码:

file_in="Prepaid_plan_voucher.csv"
file_out="Prepaid_plan_voucher.xml"

通过

file_in="$1" # Take the name as an argument from command line
file_out="${1%.csv}.xml" # Remove csv suffix and append xml

并从命令行为每个csv文件运行脚本,例如像这样:

$ for f in $(ls *.csv); do ./ourscript.sh $f; done

答案 1 :(得分:1)

由于在注释中已提及它,因此这里是使用XSLT 3.0的选项。

我测试过的处理器为Saxon-HE 9.8,并通过Java命令行运行。应该很容易将其合并到Shell脚本中以处理多个文件。

CSV输入(添加了额外的一行以显示对另一个空条目和带引号的条目的处理,这些条目包含不是分隔符的逗号)

name,Tariff Summary,Record ID No.,Operator Name,Circle (Service Area),list
Prepaid Plan Voucher,test_All calls 2p/s,TT07PMPV0188,Ta Te,Gu,
Prepaid Plan Voucher,test_All calls 3p/s,TT07PMPV0189,Ta Te,HR,
Prepaid Plan Voucher,,TT07PMPV0190,Ta Te,DH,"some,comma,separated,list"

XSLT 3.0

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" expand-text="yes">
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="csv-uri"/>
  <xsl:param name="csv-encoding" select="'UTF-8'"/>

  <xsl:template name="init">
    <TariffRecords>
      <xsl:choose>
        <xsl:when test="unparsed-text-available($csv-uri, $csv-encoding)">
          <xsl:call-template name="csv2xml"/>                               
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="error">
            <xsl:text>Error reading "{$csv-uri}" (encoding "{$csv-encoding}").</xsl:text>
          </xsl:variable>
          <xsl:message><xsl:value-of select="$error"/></xsl:message>
        </xsl:otherwise>
      </xsl:choose>
    </TariffRecords>
  </xsl:template>

  <xsl:template name="csv2xml">
    <xsl:variable name="csv_content" select="unparsed-text($csv-uri, $csv-encoding)"/>
    <xsl:analyze-string select="$csv_content" regex="\r?\n">
      <xsl:non-matching-substring>
        <xsl:if test="position() > 1"><!--ignore header-->
          <Tariff>
            <xsl:analyze-string select="concat(.,',')" regex='"([^"]*)",?|([^,]+),?'>
              <!--group 1 is wrapped in quotes-->
              <!--group 2 is not wrapped quotes-->
              <xsl:matching-substring>
                <xsl:element name="A{position()}">
                  <xsl:value-of select="(regex-group(1),regex-group(2))" separator=""/>
                </xsl:element>
              </xsl:matching-substring>
              <xsl:non-matching-substring>
                <xsl:element name="A{position()}"/>
              </xsl:non-matching-substring>
            </xsl:analyze-string>
          </Tariff>          
        </xsl:if>
      </xsl:non-matching-substring>      
    </xsl:analyze-string>
  </xsl:template>

</xsl:stylesheet>

命令行see here,以获取有关从命令行运行Saxon的更多信息)

java -cp "C:/apps/SaxonHE9-8-0-11J/saxon9he.jar" net.sf.saxon.Transform -it:init -xsl:"csv2xml.xsl" -o:"output.xml" csv-uri="input.csv"

输出

<?xml version="1.0" encoding="UTF-8"?>
<TariffRecords>
   <Tariff>
      <A1>Prepaid Plan Voucher</A1>
      <A2>test_All calls 2p/s</A2>
      <A3>TT07PMPV0188</A3>
      <A4>Ta Te</A4>
      <A5>Gu</A5>
      <A6/>
   </Tariff>
   <Tariff>
      <A1>Prepaid Plan Voucher</A1>
      <A2>test_All calls 3p/s</A2>
      <A3>TT07PMPV0189</A3>
      <A4>Ta Te</A4>
      <A5>HR</A5>
      <A6/>
   </Tariff>
   <Tariff>
      <A1>Prepaid Plan Voucher</A1>
      <A2/>
      <A3>TT07PMPV0190</A3>
      <A4>Ta Te</A4>
      <A5>DH</A5>
      <A6>some,comma,separated,list</A6>
   </Tariff>
</TariffRecords>