こんにちは。きんくまです。
電卓アプリのZippyCalcなんですが、桁区切りをやっていないのでアップデートで対応したいと思いました。
桁区切りっていう用語が正しいのかわからないのですが数値をカンマで区切る
12345 -> 12,345
というやつです。
どうやら調べてみると、数値からはデフォルトでNSNumberFormmterがあるみたいで簡単に変換できるみたいですね。すばらしい!
>> [iOS] 3桁区切りの数字文字列を生成する方法
>> [Swift]数字を三桁ごとにカンマ区切りにする – Qiita
>> iOS Tips #3 NSNumberFormatterで数値を文字列に変換する | Developers.IO
で、ZippyCalcの場合は文字列を編集した後に最後に数値や式として評価したいので、文字列は文字列のまま扱って桁区切りできればいいなと思いました。
文字列のままn桁区切りしたい
それで作ってみたのがこれです。
import Foundation public class NumberStringDigitSplitter { private class func checkFirstCharIsMinus(str:String)->(hasMinus:Bool, trimmedStr:String){ var hasMinus:Bool = false if str.characters.count == 0 { return (false, str) } let firstCharStartIndex = str.startIndex let firstCharEndIndex = firstCharStartIndex.advancedBy(1) let firstCharRange:Range = Range(start: firstCharStartIndex, end: firstCharEndIndex) let firstChar:String = str.substringWithRange(firstCharRange) var trimmedStr:String = str if firstChar == "-" { hasMinus = true trimmedStr = trimmedStr.substringFromIndex(firstCharEndIndex) } return (hasMinus, trimmedStr) } private class func splitByPoint(str:String)->(beforePointStr:String, afterPointStr:String?){ var beforePointStr:String! var afterPointStr:String? var pointRange:Range! = str.rangeOfString(".") if pointRange != nil { let afterRange:Range = Range(start: pointRange.endIndex.advancedBy(-1), end: str.endIndex) afterPointStr = str.substringWithRange(afterRange) pointRange.startIndex = str.startIndex pointRange.endIndex = pointRange.endIndex.advancedBy(-1) beforePointStr = str.substringWithRange(pointRange) }else{ beforePointStr = str } return (beforePointStr, afterPointStr) } private class func splitByDigit(str:String, digit:Int = 3)->String{ let strCount:Int = str.characters.count var strArr:[String] = [String]() var token:String! for i in 0 ..< strCount { let tokenRange = Range(start: str.startIndex.advancedBy(i), end: str.startIndex.advancedBy(i + 1)) token = str.substringWithRange(tokenRange) strArr.append(token) } strArr = strArr.reverse() var resultArr:[String] = [String]() for i in 0 ..< strCount { resultArr.append(strArr[i]) if i == 0 || i == strCount - 1{ continue } if i % digit == digit - 1 { resultArr.append(",") } } return resultArr.reverse().joinWithSeparator("") } public class func split(str:String, digit:Int = 3)->String{ var replacedStr:String = str.stringByReplacingOccurrencesOfString(",", withString: "") let checkMinusResult:(hasMinus:Bool, trimmedStr:String) = self.checkFirstCharIsMinus(replacedStr) replacedStr = checkMinusResult.trimmedStr let splitByPointResult:(beforePointStr:String, afterPointStr:String?) = self.splitByPoint(replacedStr) var beforePointStr:String = splitByPointResult.beforePointStr beforePointStr = self.splitByDigit(beforePointStr, digit: digit) var resultStr:String = beforePointStr if splitByPointResult.afterPointStr != nil { resultStr = "\(resultStr)\(splitByPointResult.afterPointStr!)" } if checkMinusResult.hasMinus { resultStr = "-\(resultStr)" } return resultStr } }
桁区切りだけなのに長くてスミマセン。
やっていることとしては、
1. マイナスがあるかどうかチェック。あればマイナスを取り除く
2. 小数点の前後で文字列を区切る
3. 整数部(小数点の前)をn桁区切りにする
4. 1,2,3でできたものをつなぎ合わせて完成
です。
使い方。digitという引数がn桁区切りでして、3を指定すると3桁区切りになります。4なら4桁区切り。
let s:String = "12345" let result:String = NumberStringDigitSplitter.split(s, digit:3) print("\(result)") //-> "12,345"
テストコード
import XCTest class NumberStringDegitSplitterTests: XCTestCase { override func setUp() { super.setUp() } override func tearDown() { super.tearDown() } func test_split_digit3(){ var testDatas:[(String, String)] = [ ("", ""), ("1", "1"), ("12", "12"), ("123", "123"), ("1234", "1,234"), ("123456", "123,456"), ("123456789012345", "123,456,789,012,345"), (",1,", "1"), ("1,2", "12"), (",12,3", "123"), ("1,234,", "1,234"), (",12,345,6", "123,456"), ("123,456,789,012,345", "123,456,789,012,345"), ("1.2.3", "1.2.3"), ("14.2.3", "14.2.3"), ("145.2.3", "145.2.3"), ("2145.2.3", "2,145.2.3"), ("123456.789", "123,456.789"), ("123456.7890.12345", "123,456.7890.12345"), ("123,45,6.78,90.12345", "123,456.7890.12345"), ("123.", "123."), (".", "."), (".345", ".345"), ("-2", "-2"), ("-23", "-23"), ("-234", "-234"), ("-2345", "-2,345"), ("-23456", "-23,456"), ("-234567", "-234,567"), ("-2345678", "-2,345,678") ] for i in 0 ..< testDatas.count { let testData:(testStr:String, answer:String) = testDatas[i] let result:String = NumberStringDigitSplitter.split(testData.testStr, digit:3) XCTAssertEqual(result, testData.answer) } } func test_split_digit4(){ var testDatas:[(String, String)] = [ ("", ""), ("1", "1"), ("12", "12"), ("123", "123"), ("1234", "1234"), ("123456", "12,3456"), ("123456789012345", "123,4567,8901,2345"), (",1,", "1"), ("1,2", "12"), (",12,3", "123"), ("1,234,", "1234"), (",12,345,6", "12,3456"), ("123,456,789,012,345", "123,4567,8901,2345"), ("1.2.3", "1.2.3"), ("14.2.3", "14.2.3"), ("145.2.3", "145.2.3"), ("2145.2.3", "2145.2.3"), ("123456.789", "12,3456.789"), ("123456.7890.12345", "12,3456.7890.12345"), ("123,45,6.78,90.12345", "12,3456.7890.12345"), ("123.", "123."), (".", "."), (".345", ".345"), ("-2", "-2"), ("-23", "-23"), ("-234", "-234"), ("-2345", "-2345"), ("-23456", "-2,3456"), ("-234567", "-23,4567"), ("-2345678", "-234,5678") ] for i in 0 ..< testDatas.count { let testData:(testStr:String, answer:String) = testDatas[i] let result:String = NumberStringDigitSplitter.split(testData.testStr, digit:4) XCTAssertEqual(result, testData.answer) } } }
■ 自作iPhoneアプリ 好評発売中!
・フォルメモ - シンプルなフォルダつきメモ帳
・ジッピー電卓 - 消費税や割引もサクサク計算!
■ LINEスタンプ作りました!
毎日使える。とぼけたウサギ