亀の甲羅2

今日もまた朝とく起きて励まなん窓に明るきありあけの月

PowerShell フォーマットに関すること(-fを使ったフォーマット指定)

1.はじめに

フォーマット変換はどの言語にもある機能だと思うが、すこしクセがあるという印象を持っている。PowerShellにおけるフォーマット変換はどんなものだろうかと思い実験してみた。切り口を簡便にするためC言語のフォーマット指定子(printfなどで使う書式指定)を念頭に置きながら使い方を学んでいこうと思う。

2.環境

PowerShellのバージョンは以下の通り。

PS > $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.18362.752
・・・以下、略・・・

3.目指すところ

  • -fを使用したフォーマット指定を学ぶ
  • C言語のprintfのフォーマット指定を念頭に確認する

※Format-Tableなどの出力整形コマンドレットは扱わない

4.やってみよう

先にも書いたが、話を単純にするために-fを使ったフォーマット指定方式で統一する。[String]::FormatToStringによるフォーマット指定などは扱わない。

4.1 -fを使用したフォーマット指定の基本

まずは基本形として、printf("%d", 10)を-fで実現してみる。

PS > "{0}" -f 10
10

{0}が置換される部分、-fの後ろにあるデータ(例では10という値)で{0}を置換する感じになる。

PS > "{0}---{0}" -f 10
10---10

{0}は単純に-fの後ろにあるデータの何番目で置き換えるかをインデックスで指定しているだけなので(0開始)、上記のようにフォーマット部分に同じインデックスのものを複数置いてもOK。

PS > $a = 10
PS > $b = 100

PS > "{0}---{1}" -f $a, $b
10---100

{0}``{1}と異なるインデックスのものを並べてもOK。その時には、-fの後ろに対応する分のデータを置いてあげればよい。{0}は$aの値で置換され、{1}は$bの値で置換される。

4.2 配列を使った例

ふと気になったので、配列を-fで指定してみた。

PS > $a = @(10, 20)

PS > $a
10
20

PS > echo ("{0}---{1}" -f $a)
10---20

配列は-fの後ろで一つ一つ書かなくても、自動的にa[0],a[1]と展開して解釈してくれるようだ。

ではハッシュテーブルならどうだろう?

PS > $b

Name                           Value                                                                                                                                                                                      
----                           -----                                                                                                                                                                                      
lemon                          80                                                                                                                                                                                         
apple                          100                                                                                                                                                                                        
banana                         50                                                                                                                                                                                         

PS > "{apple}" -f $b
文字列 入力文字列の形式が正しくありません。 の書式設定のエラーです。
発生場所 行:1 文字:1
+ "{apple}" -f $b
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: ({apple}:String) []、RuntimeException
    + FullyQualifiedErrorId : FormatError

{0}の0がただのインデックスなら、ハッシュテーブルのキーを書けば対応するバリューで置き換えてくれるかなぁ~と思い、"{apple}" -f $bとしてみたができないようだ。ご存じの方いたら教えてください。

PS > "{0}" -f $b["apple"]
100

ハッシュテーブルの場合は、上記のように愚直に-fで指定してあげる必要があるようだ(printfの本来の使い方と同じ)。

4.3 出力フォーマット指定

printfでは、"%d"(整数)、"%f"(小数)、"%s"(文字列)などで出力形式を指定する。PowerShellでは変数の型はあまり気にしたことがないが、敢えて型を指定したうえでフォーマット指定ができるか確認してみた。

PS > [byte]$a = 255  ・・・Byte型

PS > [bool]$b = 1 -eq 1 ・・・Boolean型

PS > [float]$c = 1.2345 ・・・float型(小数)

PS > [double]$d = 1.234578901 ・・・deouble型(小数)

PS > [string]$e = "abcde" ・・・string型(文字列)

PS > [int]$f = 100 ・・・int型(整数)

PS > [long]$g = 1234567890 ・・・long型(整数)


PS > "{0},{1},{2},{3},{4},{5},{6}" -f $a, $b, $c, $d, $e, $f, $g
255,True,1.2345,1.234578901,abcde,100,1234567890

想像通り、特に型に関しては気にすることがなさそう。単純に-fで指定されたデータを表示するだけのようだ。

PS > [datetime]$h = "2020/01/01 00:00:00"

PS > "{0}" -f $h
2020/01/01 0:00:00

忘れていたので追加確認。日付型も特に問題なし。(日付フォーマット指定については後述)

4.4 基数変換

printfでは%x(16進)、%d(10進)で基数変換ができた。PowerShellではどうなるか確認してみる。(差し当たり使いそうなHEX->DEC、DEC->HEXのみ調査)。

PS > "HEX {0:x}" -f 100 
HEX 64

PS > "DEC {0:d}" -f 0x64
DEC 100

{n:r} nが-fの後ろにあるデータの何番目で置き換えるかを指定するインデックス、rが基数の指定。

・参照 https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/standard-numeric-format-strings

4.5 表示桁数の指定

printfは"%10d"のように書くと表示桁を10桁とするようにできた。PowerShellではどうなるか確認してみた。(分かりやすいように両端を><で括る)

PS > ">{0}<" -f 100  ・・・桁指定なし
>100<

PS > ">{0,10}<" -f 100 ・・・10桁指定で表示
>       100<

{n,f} nが-fの後ろにあるデータの何番目で置き換えるかを指定するインデックス、fが桁数の指定。

整数型データでの検証。printf("%10d",10)と同等のことができた。

PS > ">{0}<" -f "abc"
>abc<

PS > ">{0,10}<" -f "abc"
>       abc<

文字列型データでの検証。printf("%10s","abc")と同等のことができた。

4.6 右詰・左詰の指定

PS > ">{0,10}<" -f 100 ・・・右詰め指定
>       100<

PS > ">{0,-10}<" -f 100 ・・・左詰め指定
>100       <

{n,f} nが-fの後ろにあるデータの何番目で置き換えるかを指定するインデックス、fが桁数の指定。fをマイナス値にすると「左詰め」にできる。この辺はprintfの書式指定と同じなのでなじみやすい。

PS > ">{0,10}<" -f "abc" ・・・右詰め指定
>       abc<

PS > ">{0,-10}<" -f "abc" ・・・左詰め指定
>abc       <

文字列型データでも同じ。

4.7 数字の桁数(0埋め有り/無し)

printfでは"%010d"などとすると、0埋め有り(0パディング)の10桁と指定することができた。PowerShellではどうなるか確認してみた。なお、#についても併せて確認する。

  • 0:1文字の数字(0埋め有り)
  • :1文字の数字(0埋め無し)

PS > "{0:#}" -f 0 ・・・#指定では0はデータ無しの扱いなので、0と表示されない


PS > "{0:0}" -f 0 ・・・0指定では0も0埋めされるので結果的に0が表示される
0

上記のように#を使ったときは、0の扱いには注意。

PS > "{0:##}" -f 5 ・・・2桁指定(0埋め無し)
5

PS > "{0:00}" -f 5 ・・・2桁指定(0埋め有り)
05

PS > "{0:0#}" -f 5 ・・・2桁指定(10の位0埋め有り)
05

一番上の例で分かる通り、#は該当する桁(10の位)に数字がない場合0埋めしない(スペースも無し)。一方で、二番目の例で分かる通り、0は該当する桁に数字がない場合0埋めする。

PS > "{0:0##}" -f 5
005

上記のように10の位、100の位がないデータで、10の位が#指定でも上位の100の位が0指定有ならば、いい感じに理解してくれるようだ(最上位に0があれば途中は何であれ0埋めしてくれる)。

4.8 小数、金額、パーセント(%)

(1)小数

小数の桁指定がどうなるかを確認してみた。

PS > "{0:#}" -f 5.25 ・・・整数部分の指定
5

PS > "{0:#.#}" -f 5.25 ・・・小数部分の指定を追加(小数1桁までの指定なので、小数2桁目で四捨五入される)
5.3

PS > "{0:#.##}" -f 5.25 ・・・小数部分の指定を追加(小数2桁までの指定)
5.25

PS > "{0:#.###}" -f 5.25 ・・・小数部分の指定を追加(小数3桁までの指定#)
5.25

PS > "{0:#.##0}" -f 5.25 ・・・小数部分の指定を追加(小数3桁までの指定0)
5.250

上記のように、ピリオド(.)を付けた左側が整数部、右側が小数部の桁指定となる。#と0の指定方法は、前述の内容と変わらないが、桁指定が足らない場合は四捨五入されてしまうことに注意する必要がある。また、最下位桁に0指定があれば、途中がなんであれ0埋めしてくれる。

(2)金額

金額も表示上3桁でカンマ(,)をつける場合が多い。

PS > "{0:#,###}" -f 100
100

PS > "{0:#,###}" -f 1000
1,000

PS > "{0:#,###}" -f 10000
10,000

PS > "{0:#,###}" -f 1000000
1,000,000

PS > "{0:#,###}" -f 1000000000
1,000,000,000

PS > "{0:#,###}" -f 1000000000000
1,000,000,000,000

とりあえず"{0:#,###}"という指定をしておけば、いい感じに3桁ごとにカンマを入れてくれるようだ。

(3)%パーセント

0.25が25%だが、%をフォーマット指定に使うと0.25を25%にしてくれるようだ。

PS > "{0:#%}" -f 0.25
25%

PS > "{0:#%}" -f 0.255
26%

PS > "{0:#.0%}" -f 0.255
25.5%

2番目の例をみると、小数3桁の表示指定がされていないと例のごとく四捨五入される。

4.9 符号の指定

正の数字の時には+を、負の数字の時には-を付ける方法について確認してみた。

PS > "{0:+#.##0;-#.##0}" -f 100.25
+100.250

PS > "{0:+#.##0;-#.##0}" -f -100.25
-100.250

{n:正の場合のフォーマット;負の場合のフォーマット} nが-fの後ろにあるデータの何番目で置き換えるかを指定するインデックス、その後ろにセミコロン(;)で区切って、正の場合、負の場合のそれぞれのフォーマットを指定する。

マイクロソフトの説明によると、セミコロン(;)はセクションと呼ばれるものらしく、本来は正の場合、負の場合、零の場合の3パターンを指定することができるようだ。(先の例では、このうちの正、負の2パターンを指定しただけに過ぎない。)

PS > "{0:A;B;C}" -f 1 ・・・正の場合
A

PS > "{0:A;B;C}" -f -1 ・・・負の場合
B

PS > "{0:A;B;C}" -f 0 ・・・0の場合
C

上記の例を見れば、セクションの使い方が分かるはず。

PS > "{0:+0.00;-0.00;+-0.00}" -f 1 ・・・正の場合
+1.00

PS > "{0:+0.00;-0.00;+-0.00}" -f -1 ・・・負の場合
-1.00

PS > "{0:+0.00;-0.00;+-0.00}" -f 0 ・・・0の場合
+-0.00

具体的には、上記のような使い方をするか。

・参照 https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-numeric-format-strings

4.10 日時のフォーマット

簡単なログ出力やファイル名に使うなど、現在時刻を取得して指定したフォーマットで使う場合は多い。日時のフォーマットはSQLはじめ様々な言語で指定できる。PowerShellではどうなのか確認してみた。

PS > "{0}" -f (Get-Date) ・・・単純出力
2020/06/13 15:12:20

PS > "{0:yyyyMMdd}" -f (Get-Date) ・・・年月日(0埋め有り)だけ
20200613

PS > "{0:yyyy-MM-dd}" -f (Get-Date) ・・・年月日にハイフンを入れてみた
2020-06-13

PS > "{0:yyyy-MM-dd ddd}" -f (Get-Date) ・・・曜日を入れてみた
2020-06-13 土

PS > "{0:yyyy-MM-dd dddd}" -f (Get-Date) ・・・長い曜日を入れてみた
2020-06-13 土曜日

PS > "{0:yyyy-MM-dd HH:mm:ss fff}" -f (Get-Date) ・・・時刻、ミリ秒まで入れてみた
2020-06-13 15:19:21 077

特に他言語と変わることなく普通に使えるようだ。

・参照 https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-date-and-time-format-strings

終.獲得した知識

  • -fを使用したフォーマット指定の方法