private/ConvertTo-MsecComparableName.ps1
|
function ConvertTo-MsecComparableName { <# .SYNOPSIS Folds look-alike punctuation so a name typed with a keyboard hyphen can be compared with one stored with an en dash. .DESCRIPTION Intune display names are typed by people, often in a browser on a Mac or pasted from Word, so they pick up EN DASH (U+2013), EM DASH (U+2014), curly quotes and non-breaking spaces. Those render almost identically to their ASCII counterparts and compare as different characters, which produces a name that visibly matches a list and still fails. Only punctuation is folded. Letters, digits and case are left alone - this is for characters a person cannot tell apart on screen, not for fuzzy matching. #> [CmdletBinding()] [OutputType([string])] param([Parameter(Mandatory)] [AllowEmptyString()] [string] $Name) $map = @{ [char]0x2010 = '-'; [char]0x2011 = '-'; [char]0x2012 = '-' # hyphen, non-breaking hyphen, figure dash [char]0x2013 = '-'; [char]0x2014 = '-'; [char]0x2015 = '-' # en dash, em dash, horizontal bar [char]0x2018 = "'"; [char]0x2019 = "'" # curly single quotes [char]0x201C = '"'; [char]0x201D = '"' # curly double quotes [char]0x00A0 = ' '; [char]0x2007 = ' '; [char]0x202F = ' ' # non-breaking / figure / narrow spaces } $builder = [System.Text.StringBuilder]::new() foreach ($c in $Name.ToCharArray()) { $null = $builder.Append($(if ($map.ContainsKey($c)) { $map[$c] } else { $c })) } # Trailing and repeated whitespace is invisible too, so it folds with the rest. ($builder.ToString() -replace '\s+', ' ').Trim() } function Get-MsecConfusableDifference { <# .SYNOPSIS Names the first character that differs between what was typed and what is stored, by code point, for an error a reader can act on. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] [AllowEmptyString()] [string] $Typed, [Parameter(Mandatory)] [AllowEmptyString()] [string] $Actual ) $limit = [Math]::Min($Typed.Length, $Actual.Length) for ($i = 0; $i -lt $limit; $i++) { if ($Typed[$i] -ne $Actual[$i]) { return ('position {0}: you passed U+{1:X4}, the name has U+{2:X4}' -f ($i + 1), [int]$Typed[$i], [int]$Actual[$i]) } } if ($Typed.Length -ne $Actual.Length) { return "length differs: $($Typed.Length) vs $($Actual.Length) characters" } 'no visible difference' } |