2016-12-02 48 views
-3

我有大约150个xml文件放置在需要使用新标签更新的文件夹中。使用新标签更新很多xml文件

电流:

<entry key="mergeTemplates" value="false"/> 
<entry key="sysDescriptions"/> 

新:

<entry key="mergeTemplates" value="false"/> 
    <entry key="requestable"> 
    <value> 
     <Boolean>true</Boolean> 
    </value> 
    </entry> 
    <entry key="sysDescriptions"> 

我也尝试Java的 “替代” 的方法。但无法完成它。 在Unix上也尝试了“sed”命令。

任何建议的最佳途径或工具来完成此?

回答

0

这绝不是一个有效的解决方案,但它应该适用于150个文件。如果你有SSD,它应该一眨眼的功夫。

它假设你在不同的行上有标签,并且在每个条目键=“mergeTemplates”后都应该插入新的标签(如果不是,根据情况,可以稍微修改代码以使用Matcher和分块读取行或读两行来检测第二个标签)。

public void addTextAfterLine(String inputFolder, String prefixLine, 
     String text) throws IOException { 
    // iterate over files in input dir 
    try (DirectoryStream<Path> dirStream = Files 
      .newDirectoryStream(new File(inputFolder).toPath())) { 
     for (Path inputPath : dirStream) { 
      File inputFile = inputPath.toFile(); 
      String inputFileName = inputFile.getName(); 
      if (!inputFileName.endsWith(".xml") || inputFile.isDirectory()) 
       continue; 
      File outputTmpFile = new File(inputFolder, inputFile.getName() 
        + ".tmp"); 
      // read line by line and write to output 
      try (BufferedReader inputReader = new BufferedReader(
        new InputStreamReader(new FileInputStream(inputFile), 
          StandardCharsets.UTF_8)); 
        BufferedWriter outputWriter = new BufferedWriter(
          new OutputStreamWriter(new FileOutputStream(
            outputTmpFile), StandardCharsets.UTF_8))) { 
       String line = inputReader.readLine(); 
       while (line != null) { 
        outputWriter.write(line); 
        outputWriter.write('\n'); 
        if (line.equals(prefixLine)) { 
         // add text after prefix line 
         outputWriter.write(text); 
        } 
        line = inputReader.readLine(); 
       } 
      } 
      // delete original file and rename modified to original name 
      Files.delete(inputPath); 
      outputTmpFile.renameTo(inputFile); 
     } 
    } 
} 

public static void main(String[] args) throws IOException { 
    final String inputFolder = "/tmp/xml/input"; 
    final String prefixLine = "<entry key=\"mergeTemplates\" value=\"false\"/>"; 
    final String newText = 
      "<entry key=\"requestable\">\n" 
        + " <value>\n" 
        + "  <Boolean>true</Boolean>\n" 
        + " </value>\n" 
        + "</entry>\n"    
      ; 
    new TagInsertSample() 
      .addTextAfterLine(inputFolder, prefixLine, newText); 
} 

您还可以使用高级编辑器(如记事本+ +在Windows上),以查找和替换文件中的命令。只需将<entry key="mergeTemplates" value="false"/>替换为<entry key="mergeTemplates" value="false"/>\n..new entry即可。

这里有很多笔记,你不应该用文本处理工具处理XML。如果您正在开发通用系统或库,以处理未知文件,则情况属实。但是,只需要以已知格式完成文件的任务,就不需要XML复杂性,文本处理也很合适。我很确信,在开发通用的生产系统时,没有人会要求“java,perl,Unix sed或任何其他的其他工具“。

+1

代码工作就像一个魅力!我正在sed,perl,java试图让它正确!谢谢。 – jatinshetty

+1

我很高兴这很有帮助。你可以接受答案,如果你愿意的话) –

0

使用sed这些东西都比较容易:

可以匹配一个正则表达式地址:

/^<entry key="mergeTemplates" value="false"\/>$/ 

见怎么也需要被转义为他们将有特殊意义的几个字符。还使用^(输入开始)和$(输入结束)。

当你有,你可以在运行命令的地址,在这种情况下,我们希望a PPEND命令:

/^<entry key="mergeTemplates" value="false"\/>$/a\ 
<entry key="requestable">\ 
    <value>\ 
    <Boolean>true</Boolean>\ 
    </value>\ 
</entry> 

这是是完整的sed脚本。要运行它,你可以将它保存在一个文件中(insert_xml.sed),并使用sed -f

sed -f insert_xml.sed input_file.xml 

使用-i标志进行就地编辑,它要么是-i(GNU)或-i ''(免费BSD) 。使用-i.bak(GNU)或-i .bak(免费BSD)将创建一个文件名的备份加上.bak

,然后写一个for循环中的文件需要更新:

for file in *.xml; do 
    sed -i.bak -f insert_xml.sed "$file" 
done 
+1

为什么downvote? – andlrc

+0

是不是我的DV,但有一种猜测,因为用'regex'解析'XML'是一种非常糟糕的做法,因为您正在使用正则表达式来处理不常规的语言。 – Sobrique

+1

@Sobrique确实如此,但有时对于简单替换就没有问题。 – andlrc

1

一般情况下,你不应该尝试使用面向行的工具来处理XML数据。使用类似xmlstarlet代替:

xmlstarlet ed -i "//entry[@key='sysDescriptions']" -t elem -n "new_entry" \ 
    -i "//new_entry" -t attr -n "key" -v "requestable" \ 
    --subnode "//new_entry" -t elem -n "value" \ 
    --subnode "//new_entry/value" -t elem -n "Boolean" \ 
    --subnode "//new_entry/value/Boolean" -t text -n "dummy" -v "true" \ 
    -r "//new_entry" -v "entry" input.xml 

出于可读性起见,我插了一个名为new_entry新的元素,最后给它改名。确保输入文件中不存在这样的元素。

+1

如果只需要处理大量具有众所周知格式的特定文件,实际上没有理由避免快速简单的纯文本处理。毕竟,XML文件内容是通用文本的一个子集。 –

+0

我不同意。 'XML'是上下文的,正则表达式不是。因此,正则表达式的解决方案将变得脆弱和不稳定,因为'XML'可以通过一系列完美有效的方式改变格式,从而混乱地破坏正则表达式。 – Sobrique

+0

我完全同意,如果你开发一个库或一个生产系统。但是如果你只需要更新你的特定文件和特定的数据,并不总是需要过度复杂它,并设计所有的钟声和哨声。在这种情况下,没有正则表达式,只是找到并替换文本行。 –

1

你已经标记了perl,所以我会提供一个perl解决方案。我可以提供的最好的建议一般是使用解析器,因为XML是一种可解析的语言,存在很好的解析器。对于这类工作,我特别喜欢XML::TwigXML::LibXML也很不错,但不会进行就地编辑)。

我强烈建议您避免使用正则表达式 - XML is not well suited to parsing via regex, because it's contextual and regex isn't

这里有一堆对XML可以做出的完全有效的更改,比如一元标记,缩进和行分割等,它们在语义上相同,但是混乱地打破了正则表达式。因此,未来人们做出的改变 - 就他们所关心的是重新格式化XML而言是有效的/微不足道的 - 将因为脚本无法正确处理而打破“下游”。此外 - xpath是很像正则表达式,但上下文,因此非常适合解析/处理XML

#!/usr/bin/env perl 
use warnings; 
use strict; 

use XML::Twig; 

my $twig = XML::Twig -> parse (\*DATA); 

my $to_insert = XML::Twig::Elt -> new ( 'entry', {key => "requestable"}); 
$to_insert -> insert_new_elt ('value') -> insert_new_elt('Boolean', "true"); 

print "Generated new XML:\n"; 
$to_insert -> print; 

my $insert_this = $to_insert -> cut; 

my $insert_after = $twig -> findnodes ('//entry[@key="mergeTemplates"]',0); 
$to_insert -> paste (after => $insert_after); 

print "Generated XML:\n"; 
$twig -> set_pretty_print('indented'); 
$twig -> print; 


__DATA__ 
<xml> 
<entry key="mergeTemplates" value="false"/> 
<entry key="sysDescriptions"/> 
</xml> 

这可以适于使用XML::Twigparsefile_inplace方法相当轻易:

#!/usr/bin/env perl 
use warnings; 
use strict; 
use XML::Twig; 

sub insert_merge { 
    my ($twig, $insert_after) = @_; 

    my $to_insert = XML::Twig::Elt->new('entry', { key => "requestable" }); 
    $to_insert->insert_new_elt('value')->insert_new_elt('Boolean', "true"); 

    $to_insert->paste(after => $insert_after); 
    $twig -> flush; 
} 

my $twig = 
    XML::Twig->new(
    twig_handlers => { '//entry[@key="mergeTemplates"]' => \&insert_merge }, 
    pretty_print => 'indented'); 

#glob finds files, if you want something more extensive then File::Find::Rule 
foreach my $filename (glob ("/path/to/dir/*xml")) { 
    $twig->parsefile_inplace($filename); 
}