2

Say that I have a .txt file with lines of multiple dates/times:

5/5/2020 5:45:45 AM

5/10/2020 12:30:03 PM

And I want to find the position of all slashes in one line, then move on to the next.

So for the first line I would want it to return the value:

1 3

And for the second line I would want:

1 4

How would I go about doing this?

I currently have:

$firstslashpos = Get-Content .\Documents\LoggedDates.txt | ForEach-Object{
     $_.IndexOf("/")}

But that gives me only the first "/" on each line, and gives me that result for all lines at once. I need it to loop where I can figure out the space between each "/" for each line.

Sorry if I worded this badly.

mklement0
  • 382,024
  • 64
  • 607
  • 775
justmayo
  • 41
  • 1
  • 6
  • 2
    "I need it to loop where I can figure out the space between each "/" for each line." - That's it exactly. `ForEach-Object` is looping over the lines of `LoggedDates.txt`. You need another loop inside of `ForEach-Object` to loop over the characters of each line (`$_`). – Lance U. Matthews May 20 '20 at 16:39

2 Answers2

3

You can indeed use the String.IndexOf() method for this!

function Find-SubstringIndex
{
  param(
    [string]$InputString,
    [string]$Substring
  )

  $indices = @()

  # start at position zero
  $offset = 0

  # Keep calling IndexOf() to find the next occurrence of the substring
  # stop when IndexOf() returns -1
  while(($i = $InputString.IndexOf($Substring, $offset)) -ne -1){
    # Keep track of the index at which the substring was found
    $indices += $i
    # Update the offset, we'll want to start searching for the next index _after_ this one
    $offset = $i + $Substring.Length
  }
}

Now you can do:

Get-Content listOfDates.txt |ForEach-Object {
  $indices = Find-SubstringIndex -InputString $_ -Substring '/'
  Write-Host "Found slash at indices: $($indices -join ',')"
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Currently I'm getting an error about the section ```while(($i - $string.InexOf($substring, $offset)) -ne -1)...``` being a null value. I suspect this may be because in my file, there are five or so lines at the top that will not ever contain a "/"? How would I fix that? – justmayo May 20 '20 at 17:19
  • @justmayo You have `-` instead of `=` and `InexOf` instead of `IndexOf`. – Lance U. Matthews May 20 '20 at 17:31
  • Sorry, I think that's just a typo from my comment. I'm doing the code on a separate machine so that wasn't copy-pasted – justmayo May 20 '20 at 17:36
  • 1
    I see. I think the issue is that the function parameter is named `$InputString` but then the variable being searched is named `$string`. – Lance U. Matthews May 20 '20 at 17:37
  • Yeah that was a typo, fixed now – Mathias R. Jessen May 20 '20 at 17:41
  • So, I'm not getting anything printing to screen when I use it with Get-Content/ForEach-Object. I've tried doing write-host $indices at the end of the ForEach-Object and after I call the function, but to no avail? – justmayo May 22 '20 at 15:17
  • @justmayo Not sure what you need `Get-Content` for - the indices are already in the variable? – Mathias R. Jessen May 22 '20 at 15:22
  • @MathiasR.Jessen Right, I was just saying `Get-Content` like how you used it in your original post. My issue is that I can't find a way to show indices in the console – justmayo May 22 '20 at 15:34
1

An concise solution is to use [regex]::Matches(), which finds all matches of a given regular expression in a given string and returns a collection of match objects that also indicate the index (character position) of each match:

# Create a sample file.
@'
5/5/2020 5:45:45 AM
5/10/2020 12:30:03 PM
'@ > sample.txt

Get-Content sample.txt | ForEach-Object {

  # Get the indices of all '/' instances.
  $indices = [regex]::Matches($_, '/').Index

  # Output them as a list (string), separated with spaces.
  "$indices"

}

The above yields:

1 3
1 4

Note:

  • Input lines that contain no / instances at all will result in empty lines.

  • If, rather than strings, you want to output the indices as arrays (collections), use
    , [regex]::Matches($_, '/').Index as the only statement in the ForEach-Object script block; the unary form of ,, the array constructor operator ensures (by way of a transient aux. array) that the collection returned by the method call is output as a whole. If you omit the , the indices are output one by one, resulting in a flat array when collected in a variable.

mklement0
  • 382,024
  • 64
  • 607
  • 775