Найти несколько строк в TXT/xml файле и удалить, если критерии выполнены

Интересно, можно ли создать простой скрипт для проверки соответствия нескольких критериев и внесения необходимых изменений в файл.

Перейдем к примеру того, что у меня есть и чего я хочу достичь.

У меня есть xml файл с 4 строками - число, год, модель и человек.

Если <man> - Форд или Додж, я не хочу никаких поправок. Но если <man> - это что-то другое, то я хочу проверить, являются ли <year> или <model> "NA" и удаляют строку с "NA".

<?xml version="1.0" encoding="UTF-8"?>
<CarStuff>
    <fileName>CarExpor201217.xml</fileName>
    <numberCars>5</numberCars>
    <ref>2017XY</ref>
    <carExo id="CAR0001_01">
        <dealVen id="CAR0001_02">
            <name>John</name>
            <surname>Smith</surname>
        </dealVen>
        <soldCar id="CAR0001_03">
            <amount>1811.10</amount>
            <lotNumber>1</lotNumber>
            <year>NA</year>             - Line must be removed
            <model>NA</model>           - Line must be removed
            <man>Acura</man>
        </soldCar>
    </carExo>
    <carExo id="CAR0002_01">
        <dealVen id="CAR0002_02">
            <name>John</name>
            <surname>Smith</surname>
        </dealVen>
        <soldCar id="CAR0002_03">
            <amount>1811.10</amount>
            <lotNumber>1</lotNumber>
            <year>NA</year>         - Line must be kept
            <model>NA</model>       - Line must be kept
            <man>Ford</man>
        </soldCar>
    </carExo>
    <carExo id="CAR0003_01">
        <dealVen id="CAR0003_02">
            <name>John</name>
            <surname>Smith</surname>
        </dealVen>
        <soldCar id="CAR0003_03">
            <amount>1811.10</amount>
            <lotNumber>1</lotNumber>
            <year>1997</year>       - Line must be kept
            <model>NA</model>       - Line must be removed
            <man>Bugati</man>
        </soldCar>
    </carExo>
    <carExo id="CAR0004_01">
        <dealVen id="CAR0004_02">
            <name>John</name>
            <surname>Smith</surname>
        </dealVen>
        <soldCar id="CAR0004_03">
            <amount>1811.10</amount>
            <lotNumber>1</lotNumber>
            <year>1997</year>       - Line must be kept
            <model>NA</model>       - Line must be kept
            <man>Dodge</man>
        </soldCar>
    </carExo>
    <carExo id="CAR0005_01">
        <dealVen id="CAR0005_02">
            <name>John</name>
            <surname>Smith</surname>
        </dealVen>
        <soldCar id="CAR0005_03">
            <amount>1811.10</amount>
            <lotNumber>2</lotNumber>
            <year>NA</year>         - Line must be kept
            <model>Charger</model>  - Line must be kept
            <man>Dodge</man>
        </soldCar>
    </carExo>
    <carExo id="CAR0005_01">
        <dealVen id="CAR0005_02">
            <name>John</name>
            <surname>Smith</surname>
        </dealVen>
        <soldCar id="CAR0005_03">
            <amount>1811.10</amount>
            <lotNumber>3</lotNumber>
            <year>NA</year>         - Line must be removed
            <model>Dot</model>      - Line must be kept
            <man>Datsun</man>
        </soldCar>
    </carExo>
</CarStuff>

Благодарен за все комментарии и идеи.

+1
источник поделиться
3 ответа

Решение через XMLDom

Вы можете использовать XMLDom и XPath для поиска в так называемом NodeList для тегов <man> не содержащих строки Dodge или Ford, и проверить всех братьев и сестер, если они содержат "NA", чтобы удалить их. В приведенном ниже коде используется поздняя привязка. BTW, ваш xml в OP не был хорошо сформирован (закрывающий тег </carStuf> вместо </carStuff> Я добавил небольшую процедуру проверки синтаксического анализа, чтобы проверить это при загрузке.

Код

Option Explicit

Sub checkNA()
Dim xDoc        As Object    ' xml document
Dim noli, noli2 As Object    ' node list
Dim no, no2     As Object    ' node
Dim noMan       As Object    ' node <man> to check if no Dodge or Ford
Dim s           As String
Dim sFile       As String    ' xml file name

  sFile = ThisWorkbook.Path & "\xml\na_test.xml"  ' <<< change to your xml file name

' late binding xml
  Set xDoc = CreateObject("MSXML2.DOMDocument.6.0")
  xDoc.async = False: xDoc.validateOnParse = False
  xDoc.setProperty "SelectionLanguage", "XPath"
' load xml
  If xDoc.Load(sFile) Then
    Debug.Print "Loaded successfully"
  Else
    Dim xPE        As Object    ' Set xPE = CreateObject("MSXML2.IXMLDOMParseError")
    Dim strErrText As String
    Set xPE = xDoc.parseError
    With xPE
    strErrText = "Load error " & .ErrorCode & " xml file " & vbCrLf & _
                 Replace(.URL, "file:///", "") & vbCrLf & vbCrLf & _
                 xPE.reason & _
                 "Source Text: " & .srcText & vbCrLf & vbCrLf & _
                 "Line No.:    " & .Line & vbCrLf & _
                 "Line Pos.: " & .linepos & vbCrLf & _
                "File Pos.:  " & .filepos & vbCrLf & vbCrLf
    End With
    MsgBox strErrText, vbExclamation
    Set xPE = Nothing
    Exit Sub
  End If

' check items
  s = "carExo/soldCar"
  Set noli = xDoc.DocumentElement.SelectNodes(s)
  For Each no In noli
      Set noMan = no.SelectSingleNode("man")
      If Not noMan Is Nothing Then
         If InStr("Ford.Dodge" & ".", noMan.Text & ".") = 0 Then
            Debug.Print "delete", noMan.Text
            ' delete all subtags containing "NA" as text
            Set noli2 = no.SelectNodes("*")
            For Each no2 In noli2
                If no2.Text = "NA" Then
                   ' delete item
                     Debug.Print , no2.nodename & "=" & no2.Text
                     no2.ParentNode.RemoveChild no2
                End If
            Next no2

         Else
            ' Debug.Print "keep", noman.Text
         End If
      End If
  Next no

' save
  ' Debug.Print xDoc.XML
  xDoc.Save sFile      
' close
  Set xDoc = Nothing
End Sub

Редактировать 12/29 - Добавление

Я добавил вторую работоспособную версию части ' check items используя некоторые дополнительные XPath. Эта альтернатива просто избегает двух условий If в нормальном коде, поскольку она сужает диапазон найденных узлов в двух списках узлов.

' check items
  s = "carExo/soldCar[man!='Ford'][man!='Dodge']"   ' << (1) added condition to XPath
  Set noli = xDoc.DocumentElement.SelectNodes(s)
  For Each no In noli
      Set noMan = no.SelectSingleNode("man")
      If Not noMan Is Nothing Then
         Debug.Print "delete", noMan.Text
       ' delete all subtags containing "NA" as text
         Set noli2 = no.SelectNodes("*[.='NA']")    ' << (2)added condition to XPath
         For Each no2 In noli2
           ' delete item
             Debug.Print , no2.nodename & "=" & no2.Text
             no2.ParentNode.RemoveChild no2
         Next no2
      End If
  Next no

намек

Конечно, есть много улиц, ведущих в Рим, см. Подход @Parfait XSLT ниже.

0
источник

Просто используйте XSLT, специальный язык, предназначенный для того, чтобы сделать именно то, что вам нужно для преобразования исходного XML файла, удалив узлы в соответствии с различными критериями.

В частности, ниже выполняется преобразование Identity Transform для копирования XML как есть, а затем исключается узлы по вашим критериям для model/year/man.

XSLT (сохранить как.xsl, специальный.xml файл)

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

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="soldCar[man != 'Ford' and man != 'Dodge']">
    <xsl:copy>
        <xsl:copy-of select="amount|lotNumber"/>
        <xsl:if test="model != 'NA'">
            <xsl:copy-of select="model"/>
        </xsl:if>
        <xsl:if test="year != 'NA'">
            <xsl:copy-of select="year"/>
        </xsl:if>
        <xsl:copy-of select="man"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

VBA

Public Sub RunXSLT()
    Dim strFile As String, strPath As String
    ' REFERENCE MS XML, v6.0
    Dim xmlDoc As New MSXML2.DOMDocument60, xslDoc As New MSXML2.DOMDocument60 
    Dim newDoc As New MSXML2.DOMDocument60

    ' LOAD XML SOURCE
    xmlDoc.Load "C:\Path\To\Input.xml"

    ' LOAD XSL SOURCE
    xslDoc.Load "C:\Path\To\XSLT\Script.xsl"

   ' TRANSFORM SOURCE
   xmlDoc.transformNodeToObject xslDoc, newDoc
   newDoc.Save "C:\Path\To\Output.xml"

   ' RELEASE DOM OBJECTS
    Set xmlDoc = Nothing: Set xslDoc = Nothing: Set newDoc = Nothing
End Sub

Выход

<?xml version="1.0" encoding="utf-8"?>
<CarStuff>
  <fileName>CarExpor201217.xml</fileName>
  <numberCars>5</numberCars>
  <ref>2017XY</ref>
  <carExo id="CAR0001_01">
    <dealVen id="CAR0001_02">
      <name>John</name>
      <surname>Smith</surname>
    </dealVen>
    <soldCar>
      <amount>1811.10</amount>
      <lotNumber>1</lotNumber>
      <man>Acura</man>
    </soldCar>
  </carExo>
  <carExo id="CAR0002_01">
    <dealVen id="CAR0002_02">
      <name>John</name>
      <surname>Smith</surname>
    </dealVen>
    <soldCar id="CAR0002_03">
      <amount>1811.10</amount>
      <lotNumber>1</lotNumber>
      <year>NA</year>
      <model>NA</model>
      <man>Ford</man>
    </soldCar>
  </carExo>
  <carExo id="CAR0003_01">
    <dealVen id="CAR0003_02">
      <name>John</name>
      <surname>Smith</surname>
    </dealVen>
    <soldCar>
      <amount>1811.10</amount>
      <lotNumber>1</lotNumber>
      <year>1997</year>
      <man>Bugati</man>
    </soldCar>
  </carExo>
  <carExo id="CAR0004_01">
    <dealVen id="CAR0004_02">
      <name>John</name>
      <surname>Smith</surname>
    </dealVen>
    <soldCar id="CAR0004_03">
      <amount>1811.10</amount>
      <lotNumber>1</lotNumber>
      <year>1997</year>
      <model>NA</model>
      <man>Dodge</man>
    </soldCar>
  </carExo>
  <carExo id="CAR0005_01">
    <dealVen id="CAR0005_02">
      <name>John</name>
      <surname>Smith</surname>
    </dealVen>
    <soldCar id="CAR0005_03">
      <amount>1811.10</amount>
      <lotNumber>2</lotNumber>
      <year>NA</year>
      <model>Charger</model>
      <man>Dodge</man>
    </soldCar>
  </carExo>
  <carExo id="CAR0005_01">
    <dealVen id="CAR0005_02">
      <name>John</name>
      <surname>Smith</surname>
    </dealVen>
    <soldCar>
      <amount>1811.10</amount>
      <lotNumber>3</lotNumber>
      <model>Dot</model>
      <man>Datsun</man>
    </soldCar>
  </carExo>
</CarStuff>
+1
источник

Похоже, вам нужно удалить все строки, содержащие >NA<.

Это не вопрос программирования (так это не по теме), но здесь быстрый ответ с использованием Notepad++:

  • Ctrl + H, чтобы открыть диалог поиска.

  • В текстовом поле " Find what: введите ваше регулярное выражение:. .*>NA<.*\r?\n (где \r является необязательным, если в файле нет окончаний строки Windows).

  • Оставьте поле " Replace with: текстовое поле пустым.

  • Убедитесь, что выбран переключатель " Regular Expression " в области "Режим поиска".

  • lick Replace All и voilà! Все строки, содержащие >NA<, были удалены.

NPP repl line example

(Ответ адаптирован из этого).

0
источник

Посмотрите другие вопросы по меткам или Задайте вопрос