shawnseanshaun
shawnseanshaun

Reputation: 1107

Can't convert string to int in PowerShell

I'm attempting to convert a string to an integer using PowerShell. However, it keeps on telling me that I don't have a valid number, even though I'm sure I do.

First of all, here's how I'm getting my variable, and a printout of the type, etc. just to ensure validity:

$obj = (New-Object -TypeName PSCustomObject -Property @{
    LastSaved = $com.GetDetailsOf($_, 155).toString().trim()
})
Write-Host $obj.LastSaved
$datePart,$b,$c = $obj.LastSaved.Split(" ")
Write-Host $datePart
$intVar,$b,$c = $datePart.Split("/")
$intVar = $intVar.Trim()
$intVar -replace '\W', ''
Write-Host $intVar
Write-Host $intVar.GetType()

The output:

5/‎26/‎2016 ‏‎8:09 AM

5/26/2016

5

System.String

Here's the first method I've tried for conversion:

[int]$converted = 0
[int]::TryParse($intVar, [ref]$converted)
Write-Host $converted

And the output:

False

0

Next method:

$converted = [convert]::ToInt32($intVar, 10)

And the result:

Exception calling "ToInt32" with "2" argument(s): "Could not find any recognizable digits."

And the third method I've tried:

$converted = $intVar / 1

And the result:

Cannot convert value "‎5" to type "System.Int32". Error: "Input string was not in a correct format."

If I manually assign $intVar a value of "5" ($intVar = "5") everything works just fine, so I think there must be an issue with how I'm getting the value. But I have no idea what I could be doing wrong, as the GetType() says it is indeed a string.

EDIT: Per TobyU's answer, I've also tried $intVar = [int]$intVar, with the same result of

Cannot convert value "‎5" to type "System.Int32". Error: "Input string was not in a correct format."

EDIT: Yet another method:

$intVar = [int]::Parse($intVar)

Which gives:

Exception calling "Parse" with "1" argument(s): "Input string was not in a correct format."

EDIT 3: So apparently, as some commenters mentioned, there are invalid characters. Here is the output of a Format-Hex:

           00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000   3F 32 36                                         ?26             

Upvotes: 5

Views: 7753

Answers (3)

Safwan
Safwan

Reputation: 360

The following code will work with strings as well as with numeric values. The idea is to first test and see if the value can be converted to [int], if yes, convert it, otherwise; leave it as is.

$Converted = '5'

If ([Int]::TryParse($Converted , [Ref] $Null))
{
 $Converted  = [Convert]::ToInt32($Converted)
 Write-Host "Pass '$Converted' $($Converted.GetType())" -ForeGroundColor Green
} Else {
 Write-Host "Fail '$Converted' $($Converted.GetType())" -ForeGroundColor Red
}

Upvotes: 0

mklement0
mklement0

Reputation: 437753

Examining the error messages in your question's source text reveals that your string contains the invisible LEFT-TO-RIGHT-MARK Unicode character (U+200E), which is why the conversion fails.

Removing that character will make the conversion succeed, which in the simplest case is achieved by simply eliminating all non-digit chars. from the string:

# Simulate the input string with the invisible control char.
$intStr = [char] 0x200e + '5'

# FAILS, due to the invisible Unicode char.
[int] $intStr # -> ... "Input string was not in a correct format."

# OK - eliminate non-digits first.
# Note the required (...) for proper precedence.
[int] ($intStr -replace '\D') # -> 5

Optional reading: Examining a string's characters:

# Print the code points of the string's characters:
PS> [int[]] [char[]] $intStr
8206  # decimal equivalent of 0x200e, the LEFT-TO-RIGHT-MARK
53    # decimal equivalent of 0x54, the DIGIT FIVE

# Show the code points in hex. format and print the char.
PS> [char[]] $intStr | 
       Select-Object @{ n='CodePoint'; e={ 'U+{0}' -f ([int] $_).ToString('X4') } }, 
                     @{ n='Char';      e={ $_ } }

CodePoint Char
--------- ----
U+200E       ‎
U+0035       5

You can also use Format-Hex, but the format isn't easy to parse visually:

PS> $intStr | Format-Hex -Encoding BigEndianUnicode

                       00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000000000000000   20 0E 00 35                                       ..5            

-Encoding BigEndianUnicode (UTF16-BE) is used - even though .NET string use Unicode (UTF16-LE) - so that the invariably byte-oriented display shows the high byte of the 16-bit code units first, which reads more naturally.

Byte pair 20 0E is the first code unit, U+200E (the left-to-right mark), and 00 35 the second one, U+0035 (the digit 5).

The printed characters to the right are of limited usefulness, because they are the byte-individual interpretation of the input bytes, which only renders characters in the 8-bit range as expected (code points <= U+00FF); a 0x0 byte is represented as a .

Upvotes: 4

TobyU
TobyU

Reputation: 3908

$intVar = [int]$intVar

should work just fine in this case.

$intVar.GetType() # String
$intVar = [int]$intVar
$intVar.GetType() # Int32

Upvotes: 0

Related Questions