HiveBrain v1.2.0
Get Started
← Back to all entries
snippetshellMinor

Convert a password to a phonetic string for end users

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
convertphoneticpasswordforendusersstring

Problem

As much as I hate it, sometimes providing passwords to people has to be done electronically. When I do that I try to remove any ambiguity that might be created by the font, user etc. and provide a phonetic translation of the string. For things like 0 and O or l and I which might not be obvious at first.

The function below can take a single string or be piped a string array. I could get crazy with this later to support other languages by making some nested arrays but for now it only supports English.

```
function Get-PhoneticString{
param(
[parameter(ValueFromPipeline=$true, Mandatory=$true)]
[string]$StringToExplode,
[string]$Delimiter = " - "
)
process{
# Alphabetic conversions using military phonetics
$phoneticAlphabet = @{
"a" = "alfa"; "b" = "bravo"; "c" = "charlie"; "d" = "delta"; "e" = "echo";
"f" = "foxtrot"; "g" = "golf"; "h" = "hotel"; "i" = "india"; "j" = "juliett";
"k" = "kilo"; "l" = "lima"; "m" = "mike"; "n" = "november"; "o" = "oscar";
"p" = "papa"; "q" = "quebec"; "r" = "romeo"; "s" = "sierra"; "t" = "tango";
"u" = "uniform"; "v" = "victor"; "w" = "whiskey"; "x" = "x-ray"; "y" = "yankee";
"z" = "zulu"
}

$numberAsString = @{
"0"="zero"; "1"="one"; "2"="two"; "3"="three"; "4"="four"
"5"="five"; "6"="six"; "7"="seven"; "8"="eight"; "9"="nine"
}

$symbolAsString = @{
"!" = "Exclamation Mark"; '"' = "Double Quote"; "#" = "Number Sign"; "$" = "Dollar"; "%" = "Percent"; "&" = "Ampersand"
"'" = "Single Quote"; "(" = "Left Parenthesis"; ")" = "Right Parenthesis"; "*" = "Asterisk"; "+" = "Plus"
"," = "Comma"; "-" = "Minus"; "." = "Period"; "/" = "Slash"; ":" = "Colon"; ";" = "Semicolon"; "" = "Greater Than"; "?" = "Question Mark"; "@" = "At Sign"; "[" = "Left Square Bracket"
"\" = "Backslash"; "]" = "Right Square Bracket"; "^" = "Ca

Solution

Your code looks pretty good to me. I applaud your using datastructures to hold the mappings. A lot of developers would have used a humongous switch statement there, but I think as a general rule, datastructures are to be preferred over code as they makes things clearer and more flexible.

It's a good programming principle to break things down into smaller functions. That makes the code easier to read and maintain. It makes your code self-documenting, which means you don't need to rely on comments so much.

We can put the inner logic of your blob of code into its own function:

function wordFromChar($char)
{
    switch -Regex -CaseSensitive ($char){
        "[a-z]"{$phoneticAlphabet."$char"}
        "[A-Z]"{($phoneticAlphabet."$char").ToUpper()}
        "\d"{$numberAsString."$char"}
        "\s"{$char}# Return whitespace unaltered
        default{ $symbolAsString."$char"}
    }
}


And then call it like this:

$words = $StringToExplode.ToCharArray() |  ForEach-Object {
    wordFromChar $_
}

$words -join $Delimiter


We can improve the code in the wordFromChar function. The regular expressions such as [a-z] are repeating the information that is already contained in the hash table. That violates the DRY Principle. ("Don't repeat yourself.") This is an important principle which you can look up.

I'd be inclined to put all the hash tables into a single hash table. I don't see why there should be separate tables. Having separate tables just means we have more stuff to deal with but without any real benefits. Assuming that we make a single hash table called $charMap, we could rewrite the function like this:

function wordFromChar($char)
{
    if ($charMap.Contains($char))
    {
        $result = $charMap[$char]
    }
    else 
    {
        throw "Can't handle '$char'"    
    }

    if (isUppercase $char)
    {
        $result = $result.ToUpper()
    }

    $result
}


(If there was a good reason to keep the separate hash tables, we could certainly do that. We could just put them all in an array. That is, we would have an array of hash tables. That would make the lookup logic a little more complicated, but only by a little.)

isUppercase is a new function that could look like this:

function isUppercaseChar($char)
{
    $char -cmatch "^[A-Z]$"
}


-cmatch is a case-sensitive regular expression match.

I haven't tested any of the code above, by the way.

Code Snippets

function wordFromChar($char)
{
    switch -Regex -CaseSensitive ($char){
        "[a-z]"{$phoneticAlphabet."$char"}
        "[A-Z]"{($phoneticAlphabet."$char").ToUpper()}
        "\d"{$numberAsString."$char"}
        "\s"{$char}# Return whitespace unaltered
        default{ $symbolAsString."$char"}
    }
}
$words = $StringToExplode.ToCharArray() |  ForEach-Object {
    wordFromChar $_
}

$words -join $Delimiter
function wordFromChar($char)
{
    if ($charMap.Contains($char))
    {
        $result = $charMap[$char]
    }
    else 
    {
        throw "Can't handle '$char'"    
    }

    if (isUppercase $char)
    {
        $result = $result.ToUpper()
    }

    $result
}
function isUppercaseChar($char)
{
    $char -cmatch "^[A-Z]$"
}

Context

StackExchange Code Review Q#156218, answer score: 3

Revisions (0)

No revisions yet.