2019年7月12日金曜日

swiftで文字列の数字をformatする方法が謎

電卓プログラムで欠かせないのが数字の文字列としての表示です。

表示するためには、各国のくせに応じた表示というのが不可欠で、例えば日本であれば、小数点は「.」ですが、欧州やアラビア諸国では「,」が使われるようです。

数字区切りも、本来日本は4桁のはずが3桁が普及していますが、区切りの桁や、区切り文字も小数点と同様に国によって違っていたりします。

ということで、国に応じた数字表記というのが必要で、Objective-Cの場合は理解しましたが、Swiftでのフォーマットに関する情報が希薄です。

ということで、整理しながら以下に書いていきたいと思います。

(1)数字の入力
 電卓の場合、数字の入力は10キーで行いますので、0から9の文字と小数点から構成される文字列となります。これはあくまでも内部処理なので小数点は何でもいいのですが、おそらく標準では「.」だと思われるので、「.」と仮定しておきます。

(2)文字列の数字表記変換
 数字表記への変換はNumberFormatterというクラスがあるようです。
     
       NumberFormatterリファレンス:
       https://developer.apple.com/documentation/foundation/numberformatter

 ただし、このクラスの入力はNSNumberとなるようで、入力された文字列をNSNumberに変換してから入力してあげなくてはなりません。

 まずはDoubleに変換します。
                    let doubleValue = Double(val)
 Double()は、Doubleに変換できない場合はnilになるオプショナルバリューを返しますが、これ以前に数値となるよう文字列を確認しているため、オプショナルバインディングの記述は不要となっています。


次に、DoubleをNSNumberに変換します。
                    let nsNumberValue = NSNumber(value: doubleValue!)
 ちなみに最後の!は、オプショナルバリューを強制アンラップするということらしいですが、おそらくNSNumber()はオプショナルバユーを許さないのだと思います。コンパイラーで警告されるので、警告に従い「!」をつけました。

 最後にNumberFormatteで数字表記に変換します。
                    print(String(formatter.string(from: nsNumberValue)!))
 ここではString()はnilを許していないのかと思います。こちらもコンパイラーに指摘されるので「!」をつけています。

ちなみに上の記述で「formatter」はNumberFormatterのインスタンスのことで、利用する前に様々なプロパティを設定してフォーマットを決めていく必要があります。

ここでは暫定的に以下のものとしました。

        let formatter = NumberFormatter()
        // スタイルを指定
        formatter.numberStyle = .decimal
        // 数値の区切り文字を指定する
        formatter.groupingSeparator = ","
        // 何桁ごとに区切り文字を入れるか指定する
        formatter.groupingSize = 3
        
        formatter.locale = Locale(identifier:"ja_JP")
        
        formatter.minimumFractionDigits = 10

これらについては追ってつめていきたいと思います。Localeの設定をしているので、上の,と3は不要な気もします。

実行結果の一例です。0が10個表示されるのはどうにかしたいところです。元が文字列の数字ですので、小数点以下の数字の個数がわかればどうにかなりそうです。

1
1.0000000000
12
12.0000000000
123
123.0000000000
1234
1,234.0000000000
12345
12,345.0000000000
12345.
12,345.0000000000
12345.6
12,345.6000000000
12345.67
12,345.6700000000
12345.678
12,345.6780000000
12345.6789
12,345.6789000000
12345.678
12345.67
12345.6
12345.
12345
1234
123
12
1

そこで、formatterの小数点以下の数値の数を以下のように設定してみました。

                    if let range = val.range(of: "."){
                        let underDicimal = val.count - 1 - val.distance(from: val.startIndex, to:range.lowerBound)
                        formatter.minimumFractionDigits = underDicimal
                        print(underDicimal)
                    }

ちなみに、小数点以下の長さを-1してますけど、val.countは1からの数になりますが、範囲型は0からの数になるようで、val.countから1を引いてあげる必要があるようです。

この結果はこんな感じで、思惑どおりです。

1
12
12
123
123
1234
1,234
12345
12,345
12345.
12,345

12345.6
1
12,345.6

12345.67
2
12,345.67

12345.678
3
12,345.678

12345.6789
4
12,345.6789
12345.678
12345.67
12345.6
12345.
12345
1234
123
12
1


0 件のコメント: