tech.guitarrapc.cóm

Technical updates

PowerShellなシェル芸で文字の違いを検知してみよう

面白そうだったので気まぐれに解いてみました。
Yahoo知恵袋 - 【間違い探し(超上級)】

目grep

らしいのですが、そんな事するはずがありません (

文字の違いを探す

正攻法ならこうでしょうか。
"顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顏顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔".GetEnumerator() | ?{$_ -ne "顔"}
違いの文字だけとれます。
超簡単に[string]を[char]にしてgroupすれば
"顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顏顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔".GetEnumerator() | group -NoElement
文字数も出ますね。
Count Name
----- ----
  117 顔
    1 顏
あるいは、真面目にインデックスを探してみるなら
"顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顏顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔".GetEnumerator() | %{$i=1}{[PSCustomObject]@{i=$i++;ev=$_ -eq "顔";v=$_}} | ?{$_.ev -eq $false} |ft -a
結果です。indexと結果と値が出ます。
 i    ev v
 -    -- -
54 False 顏

文字毎に最小indexもみたい

とりあえず、文字は$stringに入れてやりましょう。
$string="顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顏顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔"
値ごとの判定はこれでどうかなと
$string.GetEnumerator() | group -NoElement | select -ExpandProperty Name | %{$m=$_;$string.GetEnumerator() | %{$i=1}{[PSCustomObject]@{i=$i++;ev=$_ -eq $m;v=$_}}} | ?{$_.ev -eq $false} | ft -a
値ごとの判定はとれそうですね。
 i    ev v
  -    -- -
 54 False 顏
  1 False 顔
  2 False 顔
  3 False 顔
  4 False 顔
  5 False 顔
  6 False 顔
  7 False 顔
  8 False 顔
  9 False 顔
 10 False 顔
 11 False 顔
 12 False 顔
 13 False 顔
 14 False 顔
 15 False 顔
 16 False 顔
 17 False 顔
 18 False 顔
 19 False 顔
 20 False 顔
 21 False 顔
 22 False 顔
 23 False 顔
 24 False 顔
 25 False 顔
 26 False 顔
 27 False 顔
 28 False 顔
 29 False 顔
 30 False 顔
 31 False 顔
 32 False 顔
 33 False 顔
 34 False 顔
 35 False 顔
 36 False 顔
 37 False 顔
 38 False 顔
 39 False 顔
 40 False 顔
 41 False 顔
 42 False 顔
 43 False 顔
 44 False 顔
 45 False 顔
 46 False 顔
 47 False 顔
 48 False 顔
 49 False 顔
 50 False 顔
 51 False 顔
 52 False 顔
 53 False 顔
 55 False 顔
 56 False 顔
 57 False 顔
 58 False 顔
 59 False 顔
 60 False 顔
 61 False 顔
 62 False 顔
 63 False 顔
 64 False 顔
 65 False 顔
 66 False 顔
 67 False 顔
 68 False 顔
 69 False 顔
 70 False 顔
 71 False 顔
 72 False 顔
 73 False 顔
 74 False 顔
 75 False 顔
 76 False 顔
 77 False 顔
 78 False 顔
 79 False 顔
 80 False 顔
 81 False 顔
 82 False 顔
 83 False 顔
 84 False 顔
 85 False 顔
 86 False 顔
 87 False 顔
 88 False 顔
 89 False 顔
 90 False 顔
 91 False 顔
 92 False 顔
 93 False 顔
 94 False 顔
 95 False 顔
 96 False 顔
 97 False 顔
 98 False 顔
 99 False 顔
100 False 顔
101 False 顔
102 False 顔
103 False 顔
104 False 顔
105 False 顔
106 False 顔
107 False 顔
108 False 顔
109 False 顔
110 False 顔
111 False 顔
112 False 顔
113 False 顔
114 False 顔
115 False 顔
116 False 顔
117 False 顔
118 False 顔
初めの違いの値1つを検索するのはこれで出来るようです。
$string.GetEnumerator() | group -NoElement | select -ExpandProperty Name -First 1 | %{$m=$_;$string.GetEnumerator() | %{$i=1}{[PSCustomObject]@{i=$i++;ev=$_ -eq $m;v=$_}}} | ?{$_.ev -eq $false} |ft -a
出てますね。
 i    ev v
 -    -- -
54 False 顏
あとは、値の違いの数分だけSelectにいれればよさそうですね。 selectに入れる違う値の数をカウントします。
$count=$string.GetEnumerator() | group -NoElement | measure -Property Name
で、selectに入れると。
$string.GetEnumerator() | group -NoElement | select -ExpandProperty Name | %{$m=$_;$string.GetEnumerator() | %{$i=1}{[PSCustomObject]@{i=$i++;ev=$_ -eq $m;v=$_}} | ?{$_.ev -eq $false}} | select -First $count.Count | sort v -Unique | ft -a
とれましたw
 i    ev v
 -    -- -
54 False 顏
 1 False 顔
string値を変えてやってみましょう。
$string="顔顔顔顔顔顏顔顔顔a顔x"
$count=$string.GetEnumerator() | group -NoElement | measure -Property Name
$string.GetEnumerator() | group -NoElement | select -ExpandProperty Name | %{$m=$_;$string.GetEnumerator() | %{$i=1}{[PSCustomObject]@{i=$i++;ev=$_ -eq $m;v=$_}} | ?{$_.ev -eq $false}} | select -First $count.Count | sort v -Unique | ft -a
ワンライナーで違いも含めて取得は上手くいかなかったですが、とりあえず取れたのでいいかなと。
 i    ev v
 -    -- -
10 False a
12 False x
 6 False 顏
 1 False 顔

まとめ

文字の違いだけならgroupで
"顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顏顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔".GetEnumerator() | group -NoElement
文字数と違いがでます。
Count Name
----- ----
  117 顔
    1 顏
違いのある文字一種のインデックスもとるならこれで
"顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顏顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔".GetEnumerator() | %{$i=1}{[PSCustomObject]@{i=$i++;ev=$_ -eq "顔";v=$_}} | ?{$_.ev -eq $false} |ft -a
結果です。indexと結果と値が出ます。
 i    ev v
 -    -- -
54 False 顏
各種文字で違いの文字と最小インデックスもとるならこれで
$string="顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顏顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔顔"
$count=$string.GetEnumerator() | group -NoElement | measure -Property Name
$string.GetEnumerator() `
    | group -NoElement `
    | select -ExpandProperty Name `
    | %{
        $m=$_;
        $string.GetEnumerator() `
            | %{$i=1}{[PSCustomObject]@{
                    i=$i++;
                    ev=$_ -eq $m;
                    v=$_
                }
            } `
        | ?{
            $_.ev -eq $false
        }
    } `
    | select -First $count.Count `
    | sort v -Unique `
    | ft -a
ででん
 i    ev v
 -    -- -
54 False 顏
 1 False 顔
楽しかったです。 p.s. うぅっ