Search code examples
.netpowershelltype-conversion

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             

Solution

  • 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 .