Regex in Powershell, line by line from txt-file?

I do have a text-file named ord.txt that looks like this:

Anna:Annamaria
Sugar:Agave
Android:Phone

I want to import line by line from the ord.txt and replace the first word with the second word (Anna will be replaced with Annamaria, but not Annasofia should not be Annamariasofia) and so on in another selected file $FileBrowser.FileName. But I can not figure out how to import line by line to get it to replace every first word to the second word from the ord.txt. Is this possible or is there other solutions that is possible to do in PowerShell?

All the first words in every line in ord.txt are the once that needs to be replaced in the other file selected. There will be about 300 words in the ord.txt file.

$editButton.Add_Click({ ((Get-Content $FileBrowser.FileName -Raw) -replace $ScriptDir\ord.txt -First 1, $ScriptDir\ord.txt -First 2) | Set-Content $FileBrowser.FileName })

I’m not entirely sure I’ve understood what you’re trying to do, but isn’t it just a case of removing everything up to and including the ‘:’

(Get-Content E:\Temp\ord.txt) -replace '^.+:','' | Out-File E:\Temp\ord2.txt

 

 

I think that I was a bit unclear on what I want. The file “ord.txt” is not the file that should be edited, its the other file “$FileBrowser.FileName” (that is selected by a button).

For Example, in the $FileBrowser.FileName, I have a text like this:

Anna is a person that is friends with Annasofia and loves Sugar.
I want the words replaced by the list from the "ord.txt", like this:
Annamaria is a person that is friends with Annasofia and loves Agave.
So I want in the line "Anna:Annamaria", that Anna is replaced by Annamaria, and Sugar:Agave, "Sugar" is replaced with "Agave".

I have tried another way to somewhat get it to work, but I cant figure it out, it should go thrugh every like and replace every word in the list in “ord.txt”

$Content = Get-Content -encoding utf8 ‘$ScriptDir</span>ord.txt’
((Get-Content $FileBrowser.FileName" -Raw) -replace ‘$Content[1]’, ‘$Content[1]’) | Set-Content $FileHtmlBrowser.FileName

But I can not figure out how to use first word before the “:” with $Content[1], and the other that is suppose to replace with the one after the “:”.

I also found that I can get the first word with this from ord.txt:

foreach ($line in $lines[1]){ Write-Host ($line.Split(":"))[0] }

and the other word with this from ord.txt:

foreach ($line in $lines[1]){ Write-Host ($line.Split(":"))[1] }

But I want it to go thrugh every $lines[ ] and do the same.

In that case its just ($line.Split(":"))[0] and ($line.Split(":"))[1], that I need in the script, but I can not figure out how to make it change the $lines number so It go through every line, not only the first one.

It looks like my first answer dissapeared somehow, but here it is:

I think that I was a bit unclear on what I want. The file “ord.txt” is not the file that should be edited, its the other file “$FileBrowser.FileName” (that is selected by a button).

For Example, in the $FileBrowser.FileName, I have a text like this:

Anna is a person that is friends with Annasofia and loves Sugar.

I want the words replaced by the list from the “ord.txt”, like this:

Annamaria is a person that is friends with Annasofia and loves Agave.

So I want in the line “Anna:Annamaria”, that Anna is replaced by Annamaria, and Sugar:Agave, “Sugar” is replaced with “Agave”.

I have tried another way to somewhat get it to work, but I cant figure it out, it should go thrugh every like and replace every word in the list in “ord.txt”

$Content = Get-Content -encoding utf8 '$ScriptDir\ord.txt'
((Get-Content $FileBrowser.FileName" -Raw) -replace '$Content[1]', '$Content[1]') | Set-Content $FileHtmlBrowser.FileName

But I can not figure out how to use first word before the “:” with $Content[1], and the other that is suppose to replace with the one after the “:”.

Since the words can land anywhere in the line, you need to figure out what the differentiating criteria will be. It seems to me that could be as long as the word to be replaced is followed by anything but another letter. For that you can use a lookahead. Either a positive lookahead to match the word as long as it’s not followed by a letter, or a negative look ahead that matches unless it’s followed by a letter. An example of each.

Positive lookahead where the next character is \W (capital W meaning not a word character)

'Anna is a person that is friends with Annasofia and loves Sugar.' -replace 'Anna(?=\W)','Annamaria'

Negative lookahead where the next character is not \w (lowercase w meaning a word character)

'Anna is a person that is friends with Annasofia and loves Sugar.' -replace 'Anna(?!\w)','Annamaria'

Now you just need to figure out how to apply each replacement pair to each line (if I understood you correctly.) Since you are applying the replacements to every line, you can do so all at once… since the criteria is a whole word followed by anything but a word character. I think it would be easiest to use Get-Content -Raw Once you have the text you can loop through each pair in the orb updating the text each time. The following example should help clarify what I mean.

$textfile = New-TemporaryFile
$orbfile = New-TemporaryFile

@'
Anna is a person that is friends with Annasofia.
Anna has an android.
Also Anna loves sugar.
'@ | Set-Content $textfile -Encoding UTF8

@'
Anna:Annamaria
Sugar:Agave
Android:Phone
'@ | Set-Content $orbfile -Encoding UTF8

$text = Get-Content $textfile -Raw

Get-Content $orbfile | foreach {
    $find,$replace = $_ -split ':'
    $text = $text -replace "$find(?!\w)",$replace
}

And now $text contains the updated text which you can write back to a file or continue doing whatever you needed to.

Write-Output $text

Annamaria is a person that is friends with Annasofia.
Annamaria has an Phone.
Also Annamaria loves Agave.

I would treat orb like a dictionary and create a hash table (which is PowerShell parlance for a dictionary) from the file. You can then loop over every key in the hash table and replace each occurance of the word:

$dictionary = @{}

$ord = Import-Csv E:\Temp\ord.txt -Delimiter ':' -header 'newWord','replacementWord'

foreach ($o in $ord) {

    $dictionary[$o.newWord] = $o.replacementWord

}

$file = Get-Content E:\Temp\testFile.txt

foreach ($key in $dictionary.Keys) {

    $file = $file -replace "\b$key\b",$dictionary[$key]

}

Write-Output $file

 

Thank you for all the answers.

Doug Maurer so for getting the script Case sensitive I have a problem, I thought I had it figured out (?=\Wi), but that does change words like “anna” to “Annamaria”. I also tried a few other ways that worked in regex101, but none worked in PowerShell. I can not figure it out. I tried all day yesterday, or regex might work different in powershell?

Anna anna gives: Annamaria Annamaria but it should be: Annamaria anna

Matt Blomfield, I do like this as well, this might be what I will be using. But I get the same problem here as above with the words. “\b$key\b” it does change Anna and anna to Annamaria, but only Anna should be changed. Wierdly in regex101 it does mark Anna and not anna.

I was thinking about upper and lower case as well, is it possible to do something like this to if all words are lowercase in the ord-file, so It matches first by all lower case and then with the first letter as uppercase if its the first of a sentence or is a name?
(($text.Text.Substring(0,1)).toupper()+($text.Text.Substring(1,($text.Text.length -1)).ToLower())

 

The -replace operator is not case sensitive by default but you can use -creplace to make it so. Just make sure the word uses the correct case in your dictionary.

 

 

Thank you Matt Bloomfield that solved everything, I have made some small editing. I did also redo the “dictionary” so it has lowercase and also first letter as uppercase.

I never heard of -creplace, so wierd, I read about -replace and didn’t come across it, not mentioned once. (That did also solved another problem I had in another project to learn powershell)

Big thanks!

This is true for all the comparison operators, see Get-Help about_comparison_operators

PS C:\> 'hello' -contains 'HELLO'
True
PS C:\> 'hello' -ccontains 'HELLO'
False
PS C:\> 'hello' -like 'hell*'
True
PS C:\> 'hello' -clike 'Hell*'
False

Althought it’s the default, you can also explicitly use case insensitive operators for example -ireplace -ilike -icontains

That gives me a lot more options!

 

A related question, is it possible to get a progressbar to work with the script? I don’t get how the progressbar work. I did follow a tutorial on how to set a timer but I can not figure out how it worked.

 

$editButton_Click = {
$progressbar1 = Get-Content $Path\ord.txt -encoding utf8 | foreach {
$find, $replace = $_ -split ‘:’
((Get-Content $FileBrowser.FileName -Raw) -creplace “$find(?=\W)”, $replace) | Set-Content $FileBrowser.FileName -encoding utf8
}}

It’s pretty straightforward with Write-Progress. Using my example above, this would be inserted at line 14:

Write-Progress -Activity "Replacing Words" -CurrentOperation "Replacing $($key) with $($dictionary[$key])" -Status "Processing $($($dictionary.keys).IndexOf($key)+1) of $($dictionary.count)"

For Doug’s version, or the code you’ve posted, you would need to change it slightly to make it easier to get the length of the file.

$editButton_Click = {
    $ord = Get-Content $Path\ord.txt -encoding utf8
    $ord | foreach {
        $find, $replace = $_ -split ':'
        Write-Progress -Activity "Replacing Words" -CurrentOperation "Replacing $($find) with $($replace)" -Status "Processing $($ord.IndexOf($_)+1) of $($ord.Length)" 
        ((Get-Content $FileBrowser.FileName -Raw) -creplace "$find(?=\W)", $replace) | Set-Content $FileBrowser.FileName -encoding utf8
    } 
}

 


The “Write-Progress” is great to get it to publish in the powershell windows.

But I want the $progressbar1 in the GUI to change and when all words are complete it will be 100%

Sorry, that wasn’t clear from the code you posted. Can you share the GUI code?

I was unclear on that part, sorry for that. I have created a powerbar in the GUI and it is called $progressbar1. Most tutorials and examples are often when transfering files and by “.count”

$editButton_Click = {
Get-Content $Path\ord.xml -encoding utf8 | foreach {
$find, $replace = $_ -split ‘:’
((Get-Content $FileBrowser.FileName -Raw) -creplace “$find(?=\W)”, $replace) | Set-Content $FileBrowser.FileName -encoding utf8
}
$oReturn = [System.Windows.Forms.Messagebox]::Show(“Done”)
}

I have somewhat got it to work, but this opens a new windows, but I want the progressbar in the GUI to change, not open in a new window.

$richtextbox1.AppendText((Write-Progress -Activity "Changing " -CurrentOperation “$($find) with $($replace)” -Status “Processing $($ord.IndexOf($_) + 1) of $($ord.Length)”));

The progress bar was quite a difficult problem to solve. The problem being the GUI and the loop run in the same thread so if you launch the GUI before the loop, the loop doesn’t run until you close the form, and if you launch the GUI after the loop, the progress bar is already at 100%.

Following a couple of tutorials linked below, the script below adds a progress bar to a form.

First time working with progress bars on forms so I’m open to suggestions on how to improve this.

Fox Deploy - Building Responsive Apps With Progess Bars

Learn-PowerShell.net - Writing Data to a UI from a Different Runspace

$syncHash = [hashtable]::Synchronized(@{})

$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"         
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)          

$psCmd = [PowerShell]::Create().AddScript({   
    Add-Type -AssemblyName System.Windows.Forms
    
    $form = New-Object -TypeName System.Windows.Forms.Form
    $form.Height = 600
    $form.Width = 600

    $progressBar = New-Object -TypeName System.Windows.Forms.ProgressBar
    $progressBar.Width = 500
    $progressBar.Top = 25
    $progressBar.Left = 40 
    $progressBar.Minimum = 0
    $progressBar.Maximum =  (Get-Content E:\temp\ord.txt).Length
    $progressBar.Value = 0
    $progressBar.Step = 1
    $progressBar.Style = 'Continuous'
    $form.Controls.Add($progressBar)

    $label = New-Object -TypeName System.Windows.Forms.Label
    $label.Width = 200
    $label.Text = ''
    $label.Left = 45
    $label.Top = 50
    $form.Controls.Add($label)

    $syncHash.Form = $form
    $syncHash.progressBar = $progressBar
    $syncHash.Label = $label
    $syncHash.Form.ShowDialog() | Out-Null
    $syncHash.Error = $Error
  
})

$psCmd.Runspace = $newRunspace
$data = $psCmd.BeginInvoke()

$ord = Get-Content E:\temp\ord.txt
$text = Get-Content E:\temp\Test.txt -Raw

$ord | foreach {

    $find,$replace = $_ -split ':'
    $text = $text -replace "$find(?!\w)",$replace    
    Start-Sleep -Milliseconds 1000
    $syncHash.progressBar.PerformStep()
    $syncHash.label.Text = "Updating $find with $replace."

}

$syncHash.label.Text = 'Done!'
$psCmd.Dispose()

 

 

 

Thank you so much! This was a bit harder for me to understand. I think I understand the script. There is a few things I have to look up and read about to understand however (mostly cause English is not my main language and many words are unknown to me)

BTW, a last thing, is it possible to output the “Write-Progress” to a “richtextbox”? Or is it possible to “mirror” the powershell screen from the background into the richtextbox?

Here is the script i use:

((Write-Progress -Activity "Changing " -CurrentOperation “$($find) with $($replace)” -Status “Processing $($ord.IndexOf($_) + 1) of $($ord.Length)”));

I did try to fix by doing this:

Function Write-Progress
{
Param ($Message)
$powershellBox.AppendText()
$powershellBox.Refresh()
$powershellBox.ScrollToCaret()
}

((Write-Progress -Activity "Changing " -CurrentOperation “$($find) with $($replace)” -Status “Processing $($ord.IndexOf($_) + 1) of $($ord.Length)”));

I did also try this:

Start-Transcript = $powershellBox.AppendText()
Write-Progress -Activity “Replacing Words” -CurrentOperation “Ersätter $($find) med $($replace)” -Status “Processing $($ord.IndexOf($_) + 1) of $($ord.Length)”
Stop-Transcript

But none of them work.

No, it’s not. Write-Progress doesn’t generate any output.