FarnkD
FarnkD

Reputation: 31

How to search multiple text files in a directory for a string of text at once

I have a ListBox with a certain amount of items in it.
For each item in the ListBox a corresponding text file exists in the file directory.

I need to search each text file (based on what's in the ListBox) for a persons name. Each text file may contain the name or it may not.
I would then like a return which text file contains the name.

I have tried this as a way to search a text file: it works, but I'm not sure how to get this to repeat based on whats in a ListBox.

Dim sFileContents As String = String.Empty
If (System.IO.File.Exists((Application.StartupPath) & "\Project_Green.txt")) Then
    sFileContents = (System.IO.File.ReadAllText((Application.StartupPath) & "\Project_Green.txt"))
End If
If sFileContents.Contains(TextBox4.Text) Then
    MessageBox.Show("yup")
Else
    MessageBox.Show("nope")
End If

Also, if it would be possible to ignore case that would be great.

Upvotes: 2

Views: 709

Answers (3)

Jimi
Jimi

Reputation: 32288

Plus a pseudo-parallel async method, for the very heavy-duty name searches.
The Async Function SearchNameInTextFiles returns a named Tuple:

(FileName As String, Index As Integer)

where FileName is the file parsed and Index is the position where the first occurrence of the specified name (theName) was found.
If no matching sub-string is found, the Index value is set to -1.

The caseSensitive parameter allows to specify whether the match should be, well, case sensitive.

You can start the search from a Button.Click async handler (or similar), as shown here.

Imports System.IO
Imports System.Threading.Tasks

Private Async Sub btnSearchFiles_Click(sender As Object, e As EventArgs) Handles btnSearchFiles.Click
    Dim filesPath = [Your files path]
    Dim theName = textBox4.Text ' $" {textBox4.Text} " to match a whole word
    Dim ext As String = ".txt"  ' Or String.Empty, if extension is already included

    Dim tasks = ListBox1.Items.OfType(Of String).
        Select(Function(f) SearchNameInTextFiles(Path.Combine(filesPath, f & ext), theName, False)).ToList()
    Await Task.WhenAll(tasks)

    Dim results = tasks.Where(Function(t) t.Result.Index >= 0).Select(Function(t) t.Result).ToList()
    results.ForEach(Sub(r) Console.WriteLine($"File: {r.FileName}, Position: {r.Index}"))
End Sub

Private Async Function SearchNameInTextFiles(filePath As String, nameToSearch As String, caseSensitive As Boolean) As Task(Of (FileName As String, Index As Integer))
    If Not File.Exists(filePath) then Return (filePath, -1)
    Using reader As StreamReader = New StreamReader(filePath)
        Dim line As String = String.Empty
        Dim linesLength As Integer = 0
        Dim comparison = If(caseSensitive, StringComparison.CurrentCulture,
                            StringComparison.CurrentCultureIgnoreCase)

        While Not reader.EndOfStream
            line = Await reader.ReadLineAsync()
            Dim position As Integer = line.IndexOf(nameToSearch, comparison)
            If position > 0 Then Return (filePath, linesLength + position)
            linesLength += line.Length
        End While
        Return (filePath, -1)
    End Using
End Function

Upvotes: 1

Saeed Sayyadipour
Saeed Sayyadipour

Reputation: 550

You can do these simple steps for your purpose:

  1. First get all text files in the application's startup directory.
  2. Then iterate over all names in the ListBox and for each one, search in all text files to find the file that contains that name.

To make the process case-insensitive, we first convert names and text file's contents to "lower case" and then compare them. Here is the full code:

    Private Sub findTextFile()

        '1- Get all text files in the directory
        Dim myDirInfo As New IO.DirectoryInfo(Application.StartupPath)
        Dim allTextFiles As IO.FileInfo() = myDirInfo.GetFiles("*.txt")

        '2- Iterate over all names in the ListBox
        For Each name As String In ListBox1.Items

            'Open text files one-by-one and find the first text file that contains this name
            Dim found As Boolean = False 'Changes to true once the name is found in a text file
            Dim containingFile As String = ""
            For Each file As IO.FileInfo In allTextFiles
                If System.IO.File.ReadAllText(file.FullName).ToLower.Contains(name.ToLower) Then 'compares case-insensitive
                    found = True
                    containingFile = file.FullName
                    Exit For
                End If
            Next

            'Found?
            If found Then
                MsgBox("The name '" + name + "' found in:" + vbNewLine + containingFile)
            Else
                MsgBox("The name '" + name + "' does not exist in any text file.")
            End If
        Next
    End Sub

Upvotes: 0

user10216583
user10216583

Reputation:

If you have a bunch of files in a directory and you have their names in a ListBox, and you want to search their contents for something.

One liner query:

Imports System.IO
'...

Sub TheCaller()
    Dim dir = My.Application.Info.DirectoryPath
    Dim ext = ".txt" ' If the extensions are trimmed in the list.
    Dim find = TextBox4.Text
    Dim files = Directory.EnumerateFiles(dir).Where(Function(x) ListBox1.Items.Cast(Of String).
        Any(Function(y) String.Concat(y, ext).
        Equals(Path.GetFileName(x),
        StringComparison.InvariantCultureIgnoreCase) AndAlso File.ReadLines(x).
        Any(Function(z) z.IndexOf(find, StringComparison.InvariantCultureIgnoreCase) >= 0))).ToList

    ListBox2.Items.Clear()
    ListBox2.Items.AddRange(files.Select(Function(x) Path.GetFileNameWithoutExtension(x)).ToArray)
End Sub

Or if you prefer the For Each loop:

Sub Caller()
    Dim dir = My.Application.Info.DirectoryPath
    Dim find = TextBox4.Text
    Dim files As New List(Of String)

    For Each f As String In ListBox1.Items.Cast(Of String).
        Select(Function(x) Path.Combine(dir, $"{x}.txt"))

        If File.Exists(f) AndAlso
        File.ReadLines(f).Any(Function(x) x.IndexOf(find,
                                StringComparison.InvariantCultureIgnoreCase) <> -1) Then
            files.Add(f)
        End If
    Next

    ListBox2.Items.Clear()
    ListBox2.Items.AddRange(files.Select(Function(x) Path.GetFileNameWithoutExtension(x)).ToArray)
End Sub

Either way, the files list contains the matches if any.

Upvotes: 2

Related Questions