RegEx для сопоставления специального шаблона в VB.net - PullRequest
1 голос
/ 08 мая 2019

У меня есть код, который извлекает текст из разных файлов SGM, используя ссылку Entity на файлы (& Ch1;).Код отлично работает для этого, но теперь он расширен для того, чтобы получать ссылки на сущности для секционированных файлов с вызовами сущностей с этим типом ссылки & Ch1-1;Это также может увеличиться до & Ch1-1-1;

Мне нужно расширить код, чтобы принимать эти новые объекты, чтобы содержимое этих файлов можно было добавить в основной файл.

Я считаю, что проблемаэто регулярное выражение, поэтому я изменил его на

Dim rx = New Regex("&Ch(?<EntityNumber>\d+?[-\d+]?)?")

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

Sub runProgram()
    Dim DirFolder As String = txtDirectory.Text
    Dim Directory As New IO.DirectoryInfo(DirFolder)
    Dim allFiles As IO.FileInfo() = Directory.GetFiles("*.sgm")
    Dim singleFile As IO.FileInfo
    Dim Response As String


    Dim Prefix As String
    Dim newMasterFilePath As String
    Dim masterFileName As String
    Dim newMasterFileName As String
    Dim startMark As String = "<!--#start#-->"
    Dim stopMark As String = "<!--#stop#-->"
    searchDir = txtDirectory.Text
    Prefix = txtBxUnique.Text
    For Each singleFile In allFiles
        If File.Exists(singleFile.FullName) Then
            Dim fileName = singleFile.FullName
            Debug.Print("file name : " & fileName)
            ' A backup first    
            Dim backup As String = fileName & ".bak"
            File.Copy(fileName, backup, True)

            ' Load lines from the source file in memory
            Dim lines() As String = File.ReadAllLines(backup)

            ' Now re-create the source file and start writing lines inside a block
            Dim insideBlock As Boolean = False
            Using sw As StreamWriter = File.CreateText(backup)
                For Each line As String In lines
                    If line = startMark Then
                        ' start writing at the line below
                        insideBlock = True
                    ElseIf line = stopMark Then
                        ' Stop writing
                        insideBlock = False
                    ElseIf insideBlock = True Then
                        ' Write the current line in the block
                        sw.WriteLine(line)
                    End If
                Next
            End Using
        End If
    Next

    masterFileName = Prefix & $"_Master_Document.sgm"
    newMasterFileName = Prefix & $"_New_Master_Document.sgm"
    newMasterFilePath = IO.Path.Combine(searchDir, newMasterFileName)

    Dim existingMasterFilePath = IO.Path.Combine(searchDir, masterFileName)


    'Read all text of the Master Document
    'and create a StringBuilder from it.
    'All replacements will be done on the
    'StringBuilder as it is more efficient
    'than using Strings directly
    Dim strMasterDoc = File.ReadAllText(existingMasterFilePath)
    Dim newMasterFileBuilder As New StringBuilder(strMasterDoc)

    'Create a regex with a named capture group.
    'The name is 'EntityNumber' and captures just the
    'entity digits for use in building the file name
    Dim rx = New Regex("&Ch(?<EntityNumber>\d+(-?\d*)*)?")
    Dim rxMatches = rx.Matches(strMasterDoc)

    For Each match As Match In rxMatches
        Dim entity = match.ToString
        'Build the file name using the captured digits from the entity in the master file
        Dim entityFileName = Prefix & $"_Ch{match.Groups("EntityNumber")}.sgm.bak"
        Dim entityFilePath = Path.Combine(searchDir, entityFileName)
        'Check if the entity file exists and use its contents
        'to replace the entity in the copy of the master file
        'contained in the StringBuilder
        If File.Exists(entityFilePath) Then
            Dim entityFileContents As String = File.ReadAllText(entityFilePath)
            newMasterFileBuilder.Replace(entity, entityFileContents)
        End If
    Next


    'write the processed contents of the master file to a different file
    File.WriteAllText(newMasterFilePath, newMasterFileBuilder.ToString)

    Dim largeFilePath As String = newMasterFilePath
    Dim lines1 = File.ReadLines(largeFilePath).ToList 'don't use ReadAllLines
    Dim reg = New Regex("\<\!NOTATION.*$|\<\!ENTITY.*$", RegexOptions.IgnoreCase)
    Dim entities = From line In lines1
                   Where reg.IsMatch(line)


    Dim dictionary As New Dictionary(Of Integer, String)
    Dim idx = -1
    For Each s In entities
        idx = lines1.IndexOf(s, idx + 1)
        dictionary.Add(idx, s.Trim)
    Next

    Dim deletedItems = 0
    For Each itm In dictionary
        lines1.RemoveAt(itm.Key - deletedItems)
        deletedItems += 1
    Next

    Dim uniqueDict = dictionary.GroupBy(Function(itm) itm.Value).
    Select(Function(group) group.First()).
    ToDictionary(Function(itm) itm.Key, Function(itm) itm.Value)

    For Each s In uniqueDict.Values
        lines1.Insert(1, s)
    Next


    Dim builtMaster As String = Prefix & "_FinalDeliverable.sgm"
    Dim newBuiltMasterFilePath = IO.Path.Combine(searchDir, builtMaster)
    Dim builtMasterDoc As String = newBuiltMasterFilePath
    Using sw As New System.IO.StreamWriter(builtMasterDoc)
        For Each line As String In lines1
            sw.WriteLine(line)
        Next
        sw.Flush()
        sw.Close()
    End Using

    'Delete the master document and new master document

    If System.IO.File.Exists(existingMasterFilePath) = True Then
        System.IO.File.Delete(existingMasterFilePath)
    End If

    If System.IO.File.Exists(newMasterFilePath) = True Then
        System.IO.File.Delete(newMasterFilePath)
    End If

    For Each filename As String In IO.Directory.GetFiles(searchDir, "*.bak")
        IO.File.Delete(filename)
    Next


    Response = MsgBox("File 'FinalDeliverable.sgm' has been created.", vbOKOnly, "SGM Status")
    If Response = vbOK Then    ' User chose Yes.
        Close()
    Else    ' User chose No.
        ' Perform some action.
    End If
End Sub

Ожидаемые результаты для файлов с именами Ch1-1Содержимое .sgm между и содержимым будет добавлено в основной файл.

Это работает для файловых сущностей, которые & Ch1;он правильно захватывает содержимое Ch1.sgm.

Спасибо за помощь, Максин

Пример кода: Master_Document.sgm

<!DOCTYPE DOC PUBLIC "-//USA-DOD//DTD 38784STD-BV7//EN"[
]>
&Ch1;
<body numcols="2">
&Ch2-1;
&Ch2-2;
&Ch2-3;
&Ch2-4;
&Ch2-5;
&Ch2-6;
&Ch2-7;
&Ch2-8;
&Ch2-9;
&Ch3;
</body></doc>

Пример файла SGM

 <?Pub /_gtinsert>                     
    <body numcols="2">                    
    <!--#start#-->                        
    <chapter id="Chapter_4__Procedures">  
    <title>Procedures</title>             
    <section>                             
    <title>Introduction</title>           
    <!--#stop#-->                         
    <para0 verdate="7 Never 2012" verstatu
    <title>Description</title>            
    <para>This chapterfor the following:  

1 Ответ

1 голос
/ 08 мая 2019

Оказывается, проблема в том, что &Ch(?<EntityNumber>\d+?[-\d+]?)? соответствует &Ch, а затем одна или несколько, но как можно меньше цифр (с \d+?), а затем необязательный одиночный -, цифра или + условное обозначение. То есть после &Ch сопоставлялась только 1 цифра (так как в ваших случаях всегда есть цифра), а затем - сопоставлялась, если она следовала, и затем сопоставление прекращалось.

Используйте

Dim rx = New Regex("&Ch(?<EntityNumber>\d+(?:-\d+)*);")

См. Демонстрационную версию regex и график регулярных выражений:

enter image description here

...