表題の事案には、シェル芸で試みている時に巡り会っていたのです。 さてちょうど、いつもの問題出題サイトで表題の試みがありました。
Convert a string to a character array
たまたま経験則で答えが出てたのですが、改めて勉強になりました。 さっそく見てみます。
お題目
"PowerShell"というStringを、一文字ずつの
Char[]に変換しなさい。
つまり、こうなるはずです。
P o w e r S h e l l
縛り
- [char[]]へのキャストは禁止
- String型が持つ.ToCharArrayメソッドは禁止
ソースにすると、これが禁止です。
[char[]]"PowerShell" #禁止 ("PowerShell").ToCharArray() #禁止
型を調べてみましょう。
([char[]]"PowerShell").gettype().FullName ("PowerShell").ToCharArray().gettype().FullName
結果はChar[]型ですね。
System.Char[] System.Char[]
コード例
一文字ずつ取り出す方法は、いくつか考えられます。
- .GetEnumetrator()を利用
- .GetEnumetrator()を利用して順次配列にいれていく
- 配列(Array)にして一字ずつ取り出す
- 正規表現して、結果をSplitメソッドへ
- Splitメソッドを使う
それぞれを見ていきます。
.GetEnumetrator()を利用
まずはソースから。
$Enumerator="PowerShell".GetEnumerator()
実行してみましょう。
"------Enumerator" $Enumerator.length $Enumerator.GetType().Name "------Enumerator" $Enumerator "------Enumerator"
結果です。
GetEnumerator()では型がCharEnumeratorになっていることが分かります。あと、順次Enumeratorしてるので…結果が変数には何も出力されていません。
------Enumerator 1 1 1 1 1 1 1 1 1 1 CharEnumerator ------Enumerator ------Enumerator
これは正常な動きです。
仮に出力したいならば変数に入れず実行すればいいわけです。つまりこうですね。
"PowerShell".GetEnumerator()
期待通り出力します。
P o w e r S h e l l
.GetEnumetrator()を利用して順次配列にいれていく
ソースです。
$EnumeratorArray=@() $EnumeratorArray += "PowerShell".GetEnumerator()
実行してみましょう。
"------EnumeratorArray" $EnumeratorArray.length $EnumeratorArray.GetType().Name "------EnumeratorArray" $EnumeratorArray "------EnumeratorArray"
結果です。Char[]ではありませんが、配列に入れているのでObject[]になっていますね。
これならまぁ何とかです。変数にも格納できています。
------EnumeratorArray
10
Object[]
------EnumeratorArray
P
o
w
e
r
S
h
e
l
l
------EnumeratorArray
配列(Array)にして一字ずつ取り出す
インデックスを直接書かない理由は、他の文字でも流用するためです。毎度文字の度に変えるのはナンセンスです。
$Array=($z="Powershell")[0..$z.Length]
実行してみましょう。
"------Array" $Array.length $Array.GetType().Name "------Array" $Array "------Array"
結果です。object[]型ですが一応上手くいきますね。
------Array
10
Object[]
------Array
P
o
w
e
r
s
h
e
l
l
------Array
正規表現して、結果をSplitメソッドへ
ソースです。
$regexsplit=[regex]::split("PowerShell","")
実行してみましょう。
"------regexsplit" $regexsplit.length $regexsplit.GetType().Name "------regexsplit" $regexsplit "------regexsplit"
結果です。突込みどころが多いしアウトです。
まず、Splitメソッドで分割すると、結果はstring型になります。
加えて前後に余計な空白文字が1文字ずつ入って12文字になっています。
------regexsplit
12
String[]
------regexsplit
P
o
w
e
r
S
h
e
l
l
------regexsplit
Splitメソッドを使う
ソースです。
$split='PowerShell'-split'\B'
実行してみましょう。
"------split" $split.length $split.GetType().Name "------split" $split "------split"
結果です。
先ほど同様の突込みでアウトです。当然、Splitメソッドで分割すると結果はstring型になります。
前後の余白が入らないだけマシですが。
------split
10
String[]
------split
P
o
w
e
r
S
h
e
l
l
------split
まとめ
配列に.GetEnumerator()でいれるか、$x[インデックス指定]を入れることでobject[]型は取得できます。Splitはstringになりますので注意です。
ありがたいことに、公開して10分で、某星人から指摘がありました。
今回の例でいうとソースはこうです。
$linq =[System.Linq.Enumerable]::ToArray("PowerShell")
実行してみます。
"------linq" $linq.length $linq.GetType().FullName "------linq" $linq "------linq"
結果です。
------linq
10
System.Char[]
------linq
P
o
w
e
r
S
h
e
l
l
------linq
おまけ
lからPまで、逆さに取得したい場合は?簡単な方法は配列のインデックスを逆にたどる方法です。
ソースはこうです。
($zz="Powershell")[-1..-($zz.Length)]
結果です。
l l e h s r e w o P
これも星人はLinqでいくのが決まりということなのは最もでした。
ソースはこうです。
[System.Linq.Enumerable]::Reverse('PowerShell')
結果です。
l l e h s r e w o P
トドメの一言でした。真理です。