snippetshellMinor
Convert a password to a phonetic string for end users
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
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:
And then call it like this:
We can improve the code in the wordFromChar function. The regular expressions such as
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
(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.)
I haven't tested any of the code above, by the way.
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 $DelimiterWe 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 $Delimiterfunction 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.