Entering a string with new line characters into excel using powershell - powershell

I have a string str.
$str="abcd_1
abcd_2
abcd_3"
First in the for-loop I am concatenating the string and making a full string of Id's with a carriage return+newline character.
And on splitting I am using just the new line character.
I am getting a space in front in the data which is entered from the second cell.
for($intRow = $trow ; $intRow -le $maxRow ; $intRow++){
$codeName = $currentCode
$fin = $codeName + "_" + $i + "`r`n"
$finCode=$finCode+$fin
$i= $i + 1
}
$currentSheet.Cells.Item($fRow,$currentCol).Value2 = $finCode
$clipboardData = $finCode.Split("`n").TrimStart()
$newClipboardData = $clipboardData.Where({$_.TrimStart() -ne ""}).ForEach({$_.TrimStart()})
[System.Windows.Forms.Clipboard]::SetText($newClipboardData)
$currentSheet.Cells.Item($fRow,$currentCol).Select() | Out-Null
$currentSheet.Paste() | Out-Null

Just to be more clear and precise. This example shows how to split the string by new line character. Just increase the $intRow every time to write in next row:
ForEach($strValue In $str.Split("`n"))
{
$currentSheet.Cells.Item($intRow,$currentCol).Value2 = $strValue
$intRow++
}
New Code:
[System.Windows.Forms.Clipboard]::SetText($str.Split("`n"))
$currentSheet.Cells.Item($intRow,$currentCol).Select() | Out-Null
$currentSheet.Paste() | Out-Null

You can eliminate spaces using trim. But splitting using 'r'n is not giving me correct data. It is returning a single line.:
$clipboardData = $str.Split("`n").TrimStart()
$newClipboardData = $clipboardData.Where({$_.TrimStart() -ne ""}).ForEach({$_.TrimStart()})
[System.Windows.Forms.Clipboard]::SetText($newClipboardData)
When I checked the contents of the values it does not have any leading spaces but it shows a single space when pasted in excel.
$newClipboardData | %{ "-$_-" }
-Some Text-
-Some text With spaces -
-Some text again-

Related

How can I loop through each record of a text file to replace a string of characters

I have a large .txt file containing records where a date string in each record needs to be incremented by 2 days which will then update the field to the right of it which contains dashes --------- with that date. For example, a record contains the following record data:
1440149049845_20191121000000 11/22/2019 -------- 0.000 0.013
I am replacing the -------- dashes with 11/24/2019 (2 days added to the date 11/22/2019) so that it shows as:
1440149049845_20191121000000 11/22/2019 11/24/2019 0.000 0.013
I have the replace working on a single record but need to loop through the entire .txt file to update all of the records. Here is what I tried:
$inputRecords = get-content '\\10.12.7.13\vipsvr\Rancho\MRDF_Report\_Report.txt'
foreach ($line in $inputRecords)
{
$item -match '\d{2}/\d{2}/\d{4}'
$inputRecords -replace '-{2,}',([datetime]$matches.0).adddays(2).tostring('MM/dd/yyyy') -replace '\b0\.000\b','0.412'
}
I get an PS error stating: "Cannot convert null to type "System.DateTime"
I'm sorry but why are we using RegEx for something this simple?
I can see it if there are differently formatted lines in the file, you'd want to make sure you aren't manipulating unintended lines, but that's not indicated in the question. Even still, it doesn't seem like you need to match anything within the line itself. It seems like it's delimited on spaces which would make a simple split a lot easier.
Example:
$File = "C:\temp\Test.txt"
$Output =
ForEach( $Line in Get-Content $File)
{
$TmpArray = $Line.Split(' ')
$TmpArray[2] = (Get-Date $TmpArray[1]).AddDays(2).ToString('M/dd/yyyy')
$TmpArray -join ' '
}
The 3rd element in the array do the calculation and reassign the value...
Notice there's no use of the += operator which is very slow compared to simply assigning the output to a variable. I wouldn't make a thing out of it but considering we don't know how big the file is... Also the String format given before 'mm/dd/yyyy' will result in 00 for the month like for example '00/22/2019', so I changed that to 'M/dd/yyyy'
You can still add logic to skip unnecessary lines if it's needed...
You can send $Output to a file with something like $Output | Out-File <FilePath>
Or this can be converted to a single pipeline that outputs directly to a file using | ForEach{...} instead of ForEach(.. in ..) If the file is truly huge and holding $Output in memory is an issue this is a good alternative.
Let me know if that helps.
You mostly had the right idea, but here are a few suggested changes, but not exactly in this order:
Use a new file instead of trying to replace the old file.
Iterate a line at a time, replace the ------, write to the new file.
Use '-match' instead of '-replace', because as you will see below that you need to manipulate the capture more than a simple '-replace' allows.
Use [datetime]::parseexact instead of trying to just force cast the captured text.
[string[]]$inputRecords = get-content ".\linesource.txt"
[string]$outputRecords
foreach ($line in $inputRecords) {
[string]$newLine = ""
[regex]$logPattern = "^([\d_]+) ([\d/]+) (-+) (.*)$"
if ($line -match $logPattern) {
$origDate = [datetime]::parseexact($Matches[2], 'mm/dd/yyyy', $null)
$replacementDate = $origDate.adddays(2)
$newLine = $Matches[1]
$newLine += " " + $origDate.toString('mm/dd/yyyy')
$newLine += " " + $replacementDate.toString('mm/dd/yyyy')
$newLine += " " + $Matches[4]
} else {
$newLine = $line
}
$outputRecords += "$newLine`n"
}
$outputRecords.ToString()
Even if you don't use the whole solution, hopefully at least parts of it will be helpful to you.
Using the suggested code from adamt8 and Steven, I added to 2 echo statements to show what gets displayed in the variables $logpattern and $line since it is not recognizing the pattern of characters to be updated. This is what displays from the echo:
Options MatchTimeout RightToLeft
CalNOD01 1440151020208_20191205000000 12/06/2019 12/10/2019
None -00:00:00.0010000 False
CalNOD01 1440151020314_20191205000000 12/06/2019 --------
None -00:00:00.0010000 False
this is the rendered output:
CalNOD01 1440151020208_20191205000000 12/06/2019 12/10/2019
CalNOD01 1440151020314_20191205000000 12/06/2019 --------
This is the code that was used:
enter image description here

Remove particular characters from lines and concatenate them

I have a problem where I need to cut specific characters from a line and then concatenate the line with the next lines, separated by commas.
Consider there is a text file abc.txt and I need the last 3 lines from the file. The last 3 lines are in the this format:
11/7/2000 17:22:54 - Hello world.
19/7/2002 8:23:54 - Welcome to the new technology.
24/7/2000 9:00:13 - Eco earth
I need to remove the starting time stamp from each line and then concatenate the lines as
Hello world.,Welcome to the new technology,Eco earth.
The time stamp is not static and I want to make use of a regex
I tried the following:
$Words = (Get-Content -Path .\abc.txt|Select-Object -last 3|Out-String)
$Words = $Words -split('-')
$regex = "[0-9]{1,2}/[0-9]{1,2}/[0-9]{1,4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}):[0-9]{1,3}"
The output I used to get is like
11/7/2000 17:22:54
Hello world
19/7/2002 8:23:54
Welcome to the new technology.
24/7/2000 9:00:13
Eco earth
There is no need to create a Regex that tries to figure out the timestamp part, because you want to skip that anyway.
This should work:
# read the file and get the last three lines as string array
$txt = Get-Content -Path 'D:\abc.txt' -Tail 3
# loop through the array and change the lines as you go
for ($i = 0; $i -lt $txt.Count; $i++) {
$txt[$i] = ($txt[$i] -split '-', 2)[-1].Trim()
}
# finally, join the array with commas
$txt -join ','
Output:
Hello world.,Welcome to the new technology.,Eco earth
try this:
Get-Content "C:\temp\example.txt" | %{
$array=$_ -split "-", 2
$array[1].Trim()
}
When you have for example : "DATE - blablabla"
If you do .Split("-") on it you get :
Date
blablabla
What you can do is $string.Split("-")[Which_Line] -> so
$string="12/15/18 08:05:10 - Hello World."
$string=$string.Split("-")[1]
Returns : Hello world. (with spaces before)
Now on string you can apply Trim() function - it removes spaces before and after your string
$string=$string.Trim()
Gives you Hello world.
For your answer, if it's static usage (always 3) :
$Words = (Get-Content -Path .\abc.txt|Select-Object -last 3|Out-String).Split("-")
$end=$Words[2].Trim() + "," + $Words[4].Trim() + "," + $Words[6].Trim()

How to split a text file's lines greater than X in powershell?

My quest is to take the contents of a text file similar to below ...
A typical line
A typical line
A line that is 5,000 characters long ...............
A typical line
A line that is 30,000 characters long ......................
And split the extremely long lines at $x characters (probably 2056) so it looks like...
A typical line
A typical line
A line that is 2056 characters long (max)
A line that is 2056 characters long (max)
A line that is 2056 characters long (max)
A typical line
A line that is 2056 characters long (max)
The rest of that 30,000 char line ... etc.
I don't know what I'm doing, this was my best guess :
$originalfile = "C:\test\file.txt"
$output = "C:\test\output.txt"
foreach($line in Get-Content $originalfile){
if ($line.length -gt 2056){
$line -split ... ???
} else {
$line | out-file -Append $output
}
}
I tried this example I found :
(Get-Content $originalfile) -join " " -split '(.{2056,}?[ |$])' | Where-Object{$_} | out-file $output
... but I could never get the output to work, it just put it in one long string but it did split them at the 2056.
A typical line A typical line A line that is 5,000
characters long ............ A Typical line A line that
is 30,000 characters long.
In a perfect world I would try to split on a space, but after two days of google searching I've basically given up and don't care if it splits words in half.
Get the console width and add a line break every width characters (this doesn't take spaces into account):
# Really long string from whatever command
$mySuperLongOutputString = "SOMETHING REALLY LONG, LONGER THAN THIS"
# Get the current console width
$consoleWidth = $Host.UI.RawUI.WindowSize.Width
# For loop to iterate over each intended line in the string
for( $i = $consoleWidth; $i -lt $mySuperLongOutputString.Length; $i += $consoleWidth ) {
# Insert string at the end of the console output
$mySuperLongOutputString = $mySuperLongOutputString.Insert( $i, "`r`n" )
# Bump the counter by two to skip counting the additional newline characters
$i += 2
}
The console width is equal to the number of columns wide your buffer is.
I did end up getting this to work (mostly). It does split a little of the first word in a line, but I probably just need to tweak the regex a little bit more.
foreach($line in Get-Content $originalfile){
if ($line.length -gt 2056){
$linearray = [regex]::split($line, '(.{2000}\s)')
for ($i=0; $i -lt $linearray.length; $i++) {
$linearray[$i] | out-file -Append $output
}
$linearray=#()
} else {
$line | out-file -Append $output
}
}
Apologies for not explaining this problem very well to begin with, my brain is just not geared for this sort of stuff.
Thank you Bender for your answer, although I could not get it to work. I'm guessing because the text file is in an array (the .insert would not work for me), but it did get me to research in a different direction.

Powershell fixed width export

I am having a text file wich uses fixed width for separating columns.
I'm loading the file and create a new column which concatinates the values of the first two columns.
The problem I have that when exporting the data I need to define a fixed column width of 13 for Column C.
Column A (3) Column B(9) Column C(13)
MMA 12345 12345_MMA
MMO 987222 987222_MMO
Basically for this example in the export I am missing 4 spaces for the first row and 3 for the second row.
Thisis my current code, which also includes a new row for MD5 creation.
# Load input data
$PreSystem = [IO.File]::ReadAllText("C:\FILE.txt")
# Initiate md5-hashing
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
$utf8 = new-object -TypeName System.Text.UTF8Encoding
# Split input data by lines
$all = $PreSystem.split("`n")
# Loop over lines
for($i = 0; $i -lt $all.length-1; $i += 1) {
# Access distinct lines
$entry = "$($all[$i])"
# Get the different parameters
$market_code = $entry.substring(1,3)
$soc = $entry.substring(4,9)
# Hash the SOC element
$hash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($soc)))
# Create desired format for each entry
$output = $hash.Replace("-","")+$soc.Replace(" ","") + "_" + $market_code + $all[$i]
# Write to file
"$output" | Out-File -Filepath C:\"C:\FILE.txt" -Append -encoding ASCII
}
Thanks in advance
You can create a custom table format using the tip explained here. Here is an example for Get-Process:
$a = #{Expression={$_.Name};Label="Process Name";width=25}, `
#{Expression={$_.ID};Label="Process ID";width=15}, `
#{Expression={$_.MainWindowTitle};Label="Window Title";width=40}
Get-Process | Format-Table $a
Basically, you build an expression through wich Format-Table will pipe
each row. Instead of taking care of the formating yourself for each row, you build a hash and pipe it through Format-Table.
It's still not quite clear to me what output you actually want to achieve, but maybe this will give you some idea.
One of the most convenient ways to get formatted string output is using the format operator (-f). You specify a format string with placeholders in curly brackets, and fill it with the values of an array:
PS C:\> '_{0}:{1}:{2}_' -f 'foo', 'bar', 'baz'
_foo:bar:baz_
Column widths can be specified in the format string like this:
PS C:\> '_{0,-5}:{1,7}:{2,-9}_' -f 'foo', 'bar', 'baz'
_foo : bar:baz _
As you can see, negative values align the column to the left, positive values align it to the right.
If there's a chance that a value is too long for the give column width you need to truncate it, e.g. with the Substring() method:
PS C:\> $s = 'barbarbar'
PS C:\> $len = [math]::Min(7, $s.Length)
PS C:\> '_{0,-5}:{1,7}:{2,-9}_' -f 'foo', $s.Substring(0, $len), 'baz'
_foo :barbarb:baz _
You can quickly have a fixed size left-aligned content string using the following code:
Write-Host "$myvariable $(" " * 60)".Substring(0,60)
this will give you a fixed width of 60 characters with the contents aligned to the left
One of the solutions is for each of the rows use this mechanism when concatenating:
$a = "MMA"
$b = "12345"
$str = "$($b)_$($a)"
if (($str.Length) -ge 13 ) {
Write-Host "$($str)"
} else {
$padStr = " " * (13 - ($str.Length))
Write-Host "$($str)$($padStr)"
}
So instead of Write-Host CmdLet you can use the appropriate CmdLet for your purpose.
Edit, after adding actual code. So the above logic would translate into:
$market_code = $entry.subString(1,3)
$soc = $entry.subString(4,9)
$str = $soc.Replace(" ", "") + "_" + $market_code
if (($str.Length) -ge 13 ) {
$output = $hash.Replace("-","") + $str + $all[$i]
} else {
$padStr = " " * (13 - ($str.Length))
$output = $hash.Replace("-","") + $str + $padStr + $all[$i]
}
You can do fixed size using next code:
$data = "Some text"
$size = 20
$str = [string]::new(' ',$size).ToCharArray()
$data.CopyTo(0,$str,0,$data.Length)
$str = $str -join ''

Check first character of each line for a specific value in PowerShell

I am reading in a text file that contains a specific format of numbers. I want to figure out if the first character of the line is a 6 or a 4 and store the entire line in an array for use later. So if the line starts with a six add the entire line into sixArray and if the line starts with a 4 add the entire line into fourArray.
How can I check the first character and then grab the remaining X characters on that line? Without replacing any of the data?
Something like this would probably work.
$sixArray = #()
$fourArray = #()
$file = Get-Content .\ThisFile.txt
$file | foreach {
if ($_.StartsWith("6"))
{
$sixArray += $_
}
elseif($_.StartsWith("4"))
{
$fourArray += $_
}
}
If you're running V4:
$fourArray,$sixArray =
((get-content $file) -match '^4|6').where({$_.startswith('4')},'Split')
Use:
$Fours = #()
$Sixes = #()
GC $file|%{
Switch($_){
{$_.StartsWith("4")}{$Fours+=$_}
{$_.StartsWith("6")}{$Sixes+=$_}
}
}
If it's me I'd just use a regex.
A pattern like this will catch everything you need.
`'^[4|6](?<Stuff>.*)$'`