Textvergleich

  • VB.NET

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Danke, dass Ihr das Thema aufgegriffen habt.
    Bin gerade wieder zum Rechner gekommen.

    Meine Aufgabe ist:
    Ich will die Unterschiede ermitteln zwischen zwei großen Textdateien (Ca. 300.000 Zeilen mit LF geteilt). Ich lese die zunächst jeweils in eine RTF-Box ein, übernehme sie dann mit String.Split in je Array um die Zeilen für den Vergleich zu gewinnen. Das geht relativ schnell.

    Und darum geht es: Wie finde ich schnell die Abweichungen?

    Danke!
    Definiere

    horstfh schrieb:

    zwei RichTextBox-Inhalte
    Ist der Inhalt verschieden, wenn der Text in der einen rot und in der anderen grün ist?
    Ist der Inhalt verschieden, wenn der eine in Arial und der andere in Courier da steht?
    Ist er verschieden, wenn da ein und dort 2 Leerzeichen stehen?
    =====
    Genügt es, den Text-Inhalt / die Wörter / die Zeilen miteinander zu vergleichen?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Wenns wirklich Text-Dateien sind, und kein Richtext, dann ist Farbe und Font in den Daten ja garnet enthalten.

    Dennoch muss "Gleichheit/Verschiedenheit" genauer definiert werden.
    1. sind 2 Zeilen gleich, auch wenn einzelne Worte in Groß/Klein-Schreibung verschieden?
    2. wenn in Datei2 eine Zeile fehlt - sind dann alle folgenden Zeilen ungleich? (weil sind ja an anderer Stelle)
    3. ist die Reihenfolge überhaupt ausschlaggebend, also gälte es als gleich, wenn in Datei2 2 Zeilen vertauscht wären?
    Die Antworten auf die Fragen ergeben sich aus meiner Antwort:

    Die zwei Dateien unterscheiden sich dadurch, dass
    • Zeilen entfallen
    • Zeilen hinzugefügt sein
    können an beliebiger Stelle im Gesamttext.

    Groß-/kleinschreibung sind natürlich ohne Bedeutung, denn wozu haben wir ToLower etc.
    Ich muss prüfen, ob eine Zeile im anderen Text steht. Wenn nicht, will ich die Abweichenden Zeilen in ein weiterers Array übernehmen.
    Die Position spielt also auch keine Rolle.
    Kommt eine Zeile im Vergleichstext einmal oder mehrfach vor, passiert gar nichts.
    Am Schluss habe ich ein Array mit den Zeilen von Text1 die in Text2 nicht enthalten sind. Der ganze Vorgang kann dann mit dem gewonnenen Array rückwärts erfolgen. Am Ende habe ich zwei Listen.
    Zumindest stelle ich mir das so vor.

    Ich habe schon mit IndexOf im zweiten Text versucht, aber das geht einfach, bei der Textlänge, zu langsam.

    Nachtrag zur Antwort von Rod: Alles ohne Bedeutung!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „horstfh“ ()

    horstfh schrieb:

    Am Schluss habe ich ein Array mit den Zeilen von Text1 die in Text2 nicht enthalten sind.

    Und ein Array zu haben, mit den Zeilen von Text2, die in Text1 nicht enthalten sind, ist nicht von Interesse?

    Oder ist genau das damit gemeint?

    horstfh schrieb:

    Der ganze Vorgang kann dann mit dem gewonnenen Array rückwärts erfolgen. Am Ende habe ich zwei Listen.

    Also ich hab eine Extensin-Funktion, die vergleicht 2 beliebige Auflistungen, und schlüsselt auf nach
    1. List1Only
    2. List2Only
    3. Mappings (d.h. Entsprechungen vorgefunden)
    Ist kniffliger Code, aber sehr flott.
    Könntest du mit so einer Klasse was anfangen?

    Ein TestAufruf könnte so aussehen:

    VB.NET-Quellcode

    1. Private Sub Test()
    2. Dim lst1 = "a b c d e".Split
    3. Dim lst2 = "g f d e h".Split
    4. Dim result = lst1.DifferenceAnalize(lst2, Function(s) s.GetHashCode)
    5. Dim lst1Only As List(Of String) = result.List1_Only ' a, b, c
    6. Dim lst2Only As List(Of String) = result.List2_Only ' g, f, h
    7. Dim mappings As List(Of Tuple(Of String, String)) = result.Mappings ' (d,d), (e,e)
    8. End Sub

    Genau so stelle ich mir das vor!!

    Ich habe (fast) alle möglichen Experimente gemacht, mit ListBox, mit RTF-Box und immer ging zwar das Einlesen recht flott. Nur die - Du sagste es - BEIDEN Vergleiche haben einfach meine Geduld gefressen. Es fehlt ganz einfach eine Compare-Methode.

    Wie geht es?
    Schon jetzt vielen Dank für alle Anregung!!
    Das ist das Modul:

    VB.NET-Quellcode

    1. Public Module Extensions
    2. <Extension()> _
    3. Public Function DifferenceAnalize(Of T1)(lst1 As IEnumerable(Of T1), lst2 As IEnumerable(Of T1), Optional keySelector As Func(Of T1, Integer) = Nothing) As DifferenceResult(Of T1, T1)
    4. Return DifferenceAnalize(lst1, lst2, keySelector, keySelector)
    5. End Function
    6. <Extension()> _
    7. Public Function DifferenceAnalize(Of T1, T2)(lst1 As IEnumerable(Of T1), lst2 As IEnumerable(Of T2), Optional keySelector1 As Func(Of T1, Integer) = Nothing, Optional keySelector2 As Func(Of T2, Integer) = Nothing) As DifferenceResult(Of T1, T2)
    8. If keySelector1 Is Nothing Then keySelector1 = Function(o As T1) o.GetHashCode
    9. If keySelector2 Is Nothing Then keySelector2 = Function(o As T2) o.GetHashCode
    10. Dim dic1 = New Dictionary(Of Integer, T1)
    11. With DifferenceAnalize
    12. .List2_Only = New List(Of T2)
    13. .Mappings = New List(Of Tuple(Of T1, T2))
    14. Dim itm1 As T1 = Nothing
    15. For Each itm1 In lst1
    16. Try
    17. dic1.Add(keySelector1(itm1), itm1)
    18. Catch ex As ArgumentException
    19. Throw New ArgumentException("all keyValues of lst1 must be unique")
    20. End Try
    21. Next
    22. Dim hsh = New HashSet(Of Integer)
    23. For Each itm2 In lst2
    24. Dim key = keySelector2(itm2)
    25. If dic1.TryGetValue(key, itm1) Then
    26. .Mappings.Add(Tuple.Create(itm1, itm2))
    27. dic1.Remove(key)
    28. Else
    29. .List2_Only.Add(itm2)
    30. If Not hsh.Add(key) Then Throw New ArgumentException("all keyValues of lst2 must be unique")
    31. End If
    32. Next
    33. .List1_Only = dic1.Values.ToList
    34. End With
    35. End Function
    36. Public Structure DifferenceResult(Of T1, T2)
    37. Public List1_Only As List(Of T1)
    38. Public List2_Only As List(Of T2)
    39. Public Mappings As List(Of Tuple(Of T1, T2))
    40. End Structure
    41. End Module

    kriegst du damit den oben gezeigten Test ans laufen?

    Versuch erstmal den Test, denn für die Dateien ist noch ein Distinct zwischenzuschalten.
    Hallo Erfinder...
    Vielen Dank für Deine Anregung. Ich habe mich mit der in Deinem Code gezeigten Materie (Hashcode) bisher gar nicht beschäftigt.
    Also Code kopiert auch vom Aufrufbeispiel. Ergebnis:
    In der Zeile

    VB.NET-Quellcode

    1. Dim result = lst1.DifferenceAnalize(lst2, Function(s) s.GetHashCode)

    Markierung am Ende und Meldung:
    Der öffentliche Member DifferenceAnalize für den Typ String() wurde nicht gefunden.

    Hast Du einen Tipp für einen, der zwar mit ALLEN VB-Versionen gearbeitet hat aber bei VB.NET beinahe gestreikt hätte.
    Du hast mich vor einiger Zeit wegen des VB6-Codes und Strict-Off schon ein wenig korrigieren müssen.
    Gruß
    Horst
    Hallo Erfinder ...
    Danke!

    Jetzt eine Info, die immer an Anfang stehen sollte:

    Ich arbeite mit VS 2010 aber hatte FX 4.6 noch NICHT installiert. Möglicherweise hat das zu dem Problem beigetragen. Installation erfolgt gerade. Dein Beispiel habe ich entpackt werde mir heute Abend Zeit zur genauen Betrachtung nehmen. Da gibt es ja noch viel zu erforschen. Aber zunächst vielen Dank.

    Ich gehe davon aus, dass wir mit diesem Angebot von Dir das Thema vorläufig abschließen sollten.
    Nochmals: Danke!!
    Bis bald!
    Horst
    Hallo Erfinder ...

    Vielen Dank für den Code.
    Etwas lachen musste ich darüber, dass der Entwickler in der Aufruf-Prozedur LateBinding einsetzt, also gegen Deine heilige Regel verstößt.
    Ich habe den Code entsprechend verändert und sende ihn Dir zur Weitergabe anstelle des anderen zu. Jetzt ist auch eine MessageBox-Ausgabe drin, um das Test-Beispiel zu vervollständigen.
    Die Schnelligkeit werde ich noch testen.
    Aber Du hast mir sehr geholfen.
    Interessant auch die zeile:

    VB.NET-Quellcode

    1. System.Media.SystemSounds.Asterisk.Play()

    mit dem man ein etwas kräftigeres Beep erzeugen kann.

    Nochmals: Vielen Dank!
    Schönes Wochenende
    Gruß

    PS: Jetzt sollte ich nur noch wissen, wie ich das ZIP-File anhänge! <X
    Wenn ichs weiß, schick ich den Code!
    Hab's doch noch gefunden.
    Dateien

    horstfh schrieb:

    ...dass der Entwickler in der Aufruf-Prozedur LateBinding einsetzt...
    stimmt doch garnet.
    oder was meinst du?
    also ich setze so gut wie nie late-binding ein, und auch in meim upload nicht.

    Zu deim zip: der ist iwie verdaddelt, und kompiliert nicht - iwelche Resource-Dateien fehlen.

    Aber ich fand meinen unveränderten Code darin - Frage: Was soll ich damit- den hab ich doch schon?

    Wärest du an einem Tool interessiert, mit dem man mit einem Click seine Sources lauffähig zippt (so mach ich das nämlich immer ;) )

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Nicht böse sein.

    In Deinem Code steht:

    VB.NET-Quellcode

    1. Private Sub AnyButton_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btReLoad.Click, btSave.Click, btTest.Click
    2. Select Case True
    3. Case sender Is btReLoad
    4. Case sender Is btSave
    5. Case sender Is btTest
    6. Test()
    7. End Select
    8. System.Media.SystemSounds.Asterisk.Play()
    9. End Sub


    Aber das stammt sicher nicht von Dir!

    Ich habe den Code neu gezipt und hänge ihn an.
    Bei dem vorherigen habe ich die falschen Zeilen übernommen.
    Der neue kann unter VB2010 gestartet werden ohne ständigen Hinweis auf die fehlende 4.6.

    Einen schönen Abend noch.
    HG
    Horst

    PS ich verwende 7Zip. Wenn Du was Besseres hast bin ich natürlich interessiert.
    Dateien
    • DemoFuerSuche.zip

      (133,03 kB, 82 mal heruntergeladen, zuletzt: )
    doch, das ist von mir.

    und wo siehst du da late-binding?

    PS: mein Tool analysiert die .Sln - Datei und zippt dann nur die Sources. Also es zippt nicht besser als 7zip - es zippt nur weniger.
    Und das Zeug läuft dann nachm entpacken - das hat in deim ersten Versuch ja net hingehauen.

    Ausserdem ists äusserst unfein ist, executables zu mit-zu-zippen - ist auf VBP auch nicht erlaubt.

    (Wobei - auch mit meim Tool kanns failen - also Projekte zippen muss man üben, bis man eine Variante raushat.)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    horstfh schrieb:

    VB.NET-Quellcode

    1. Case sender Is btReLoad
    ist nicht LateBinding, falls Du das meinst.
    Hier werden Pointer verglichen, also es wird festgestellt, ob das sendende Objekt der eine oder der andere Button ist.
    LateBinding wäre, das Objekt sender vom Typ Object mit einer Methode oder Property aufzurufen, die nicht zum Typ Object, wohl aber zum Typ Button oder Control gehört:

    VB.NET-Quellcode

    1. sender.BackGroundColor = Color.Red
    .
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!