2016-12-02 23 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); 
}