What regex can split a multi-line (potentially markdown) text along headings?

118 views Asked by At

I have a multi-line text like the following

#####1
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

#####2
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

#####3
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

#####I
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

#####II
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

(there's an empty line at the end, despite what SO thinks). I need to split this along the headings, keeping the heading text, and putting the whole thing in a dictionary/hash table/associative array/whatever you call it using the heading text as keys (they are unique).

I tried to iterate through the lines but my brain is too fried up right now to get this right -- I get each text shifted ahead by one, heading 1 ends up empty, and heading II ends up with the text from heading I.

I was thinking I could do this with a regex, so I came up with this

#####(.+)\n([\w\W\n]+?)#

which obviously captures the "next" #, thus it only captures odd headings.

Ideas? (I'm language-agnostic, so feel free to answer in the one you prefer).

2

There are 2 answers

2
Morpheu5 On

OK, that was embarrassingly easy. As it turns out, splitting on ##### was perfectly acceptable.

articles = text.split('#####').map do |e|
  a = e.split("\n")
  [a[0], a[1..-1]]
end[1..-1].map do |e|
  ["art-#{e[0]}", e[1]]
end.to_h
1
Andrew Morton On

You said any language, so in VB.NET and parsing line-by-line, which should reduce memory usage:

Option Infer On
Option Strict On

Imports System.IO
Imports System.Text

Module Module1

    Public Function GetParsedSections(s As String) As Dictionary(Of String, String)
        Dim sections As New Dictionary(Of String, String)
        ' a MemoryStream suffices here to emulate reading from a file as a stream...
        Using ms As New MemoryStream(Encoding.UTF8.GetBytes(s))
            Dim currentSectionName = ""
            Using sr As New StreamReader(ms)
                While Not sr.EndOfStream
                    Dim thisLine = sr.ReadLine()
                    If thisLine.StartsWith("#####") Then
                        ' we have a heading: update currentSectionName
                        currentSectionName = thisLine.Substring(5)
                    Else
                        ' avoid sections with no heading, i.e. preamble:
                        If currentSectionName <> "" Then
                            If sections.ContainsKey(currentSectionName) Then
                                ' note: VbCrLf represents the character sequence CRLF
                                ' add a line to the appropriate dictionary item
                                sections(currentSectionName) &= thisLine & vbCrLf
                            Else
                                ' create a new dictionary entry with the content of the current line
                                sections.Add(currentSectionName, thisLine & vbCrLf)
                            End If
                        End If
                    End If
                End While
            End Using
        End Using

        Return sections

    End Function

    Sub Main()
        Dim s = "#####1
Lo venas término que ilesa bajo abeja poniendo las tierra queman pero los los se huye entonces por muerte escaleras.

#####2

Y bajo abierta los vacía tu la me lenta talco consume quedo tierra sillas subía escaleras loca de bala mi.

Manteles y es ilesa de poniendo atrás llanura los un baja pero repartiendo los tierra venas la criaturas vacía el.

Desnudo todo quedo se los come ceniza muertos por que duro para desnuda hombrecillo aire los es los quedo bajo.

#####3
Por nino recientes arroyo quedo muelles quedo en me tu las la sillas cielo las ojos lo desnudo musgos el.

El desnudo nino los del de los luna con es vengo abrir de poniendo con fría come lentejas sillas es.

#####I
A dolor algodón buscando de de faraón apariencia cielo me los es la nino oh pasan mujer llanura de por.

Encuentro sensitivo quemadas paso los quedo musgos borrachos de recientes bajaba abierta imperturbable que al con es de y se.

#####II
Los baja comida de lenguas lenta que que y abrir quedo ballenas lo brooklyn bajaba tierra de escobazos se me.

#####1
Some more text for heading 1.

"

        Dim topics = GetParsedSections(s)

        For Each topic In topics.Keys
            Console.WriteLine("Heading: " & topic)
            Console.WriteLine(topics(topic))
        Next

        Console.ReadLine()

    End Sub

End Module

Outputs:

Heading: 1
Lo venas término que ilesa bajo abeja poniendo las tierra queman pero los los se huye entonces por muerte escaleras.

Some more text for heading 1.


Heading: 2

Y bajo abierta los vacía tu la me lenta talco consume quedo tierra sillas subía escaleras loca de bala mi.

Manteles y es ilesa de poniendo atrás llanura los un baja pero repartiendo los tierra venas la criaturas vacía el.

Desnudo todo quedo se los come ceniza muertos por que duro para desnuda hombrecillo aire los es los quedo bajo.


Heading: 3
Por nino recientes arroyo quedo muelles quedo en me tu las la sillas cielo las ojos lo desnudo musgos el.

El desnudo nino los del de los luna con es vengo abrir de poniendo con fría come lentejas sillas es.


Heading: I
A dolor algodón buscando de de faraón apariencia cielo me los es la nino oh pasan mujer llanura de por.

Encuentro sensitivo quemadas paso los quedo musgos borrachos de recientes bajaba abierta imperturbable que al con es de y se.


Heading: II
Los baja comida de lenguas lenta que que y abrir quedo ballenas lo brooklyn bajaba tierra de escobazos se me.

Which allows for additions to the content of a section later.