数値チェックをする

投稿者: | 2016年2月14日
2016年11月6日追記
以下にInt()!の”!”に関する記述が出てきますが、Swift2の頃まではこの記事が初出だったんですが、Swift3になってコードを書き直していると結構はじめのうちから出てくるようになりました。オプショナル型って言います。

詳細はオプショナル型で解説しています。

前回、数値チェックのテストケースを後回しにしたのは、数字以外のデータをInt()!に入力するとエラー停止してしまうからです。テストコード自体がエラーで停止するっていうね。。。

実はInt()の後ろにしれっとつけている”!”がこの話に関わっているんですが、ちょっとややこしそうなのでここでは割愛。

で、本題のチェックについて。幸い今回扱う数は自然数なので、文字列に0から9以外の文字が含まれていなければOK。先頭から1文字づつ走査していって数字以外の文字が見つかったらフラグセット、以降の処理を打ち切ります。日数カウンターのところで使った手ですね。

#! /usr/bin/swift
import Foundation
let arguments: [String] = CommandLine.arguments
print("Natural Number Checker(Zero is included.)")
var err: Bool = true
for var i in 1..<arguments.count {
  chkchar: for char in arguments[i].characters {
    switch char {
    case "0"..."9":
      err = true
    default:
      err = false
      break chkchar
    }
  }
  print("\(arguments[i]) is ", terminator:"")
  if !(err) {
    print("\u{001B}[0;31mNOT\u{001B}[0;30m ", terminator:"")
  }
  print("Natural Number.")
}

与えられた引数が数字か否かをチェックしてその結果を表示するという、ちょっと余分なコードを含んでいますが、肝心の数値チェックの部分は7から15行目。

これを関数化してcheckOptionクラス内で適宜実行します。具体的には年指定と月指定が正しいかどうかの確認。ここで少し仕様を拡張して、数字であるか否かに加えて指定範囲に収まっているかどうかを確認させます。

class checkOption {
  private var arguments: [String]
  private var calendarRaws: Int   // 表示月行数
  private var calendarCols: Int   // 表示月列数
  private var weekFormat: Bool    // 週表示指示 = true
  private var startYear: Int      // 年指定なし = 0 (1800...9999)
  private var startMonth: Int     // 月指定なし = 0 (1...12)
  private var adjustMonth: Int    // 表示開始月調整値
  private var optionError: Int32  // エラーなし = 0
                                  // 指定年上限越え = 1, 指定年下限越え = 2, 指定年変換エラー = 3,
                                  // 指定月上限越え = 4, 指定月下限越え = 5, 指定月変換エラー = 6,
                                  // 引数エラー = 9、エラー時は他の全てのプロパティがデフォルト値
  init(arguments: [String], calendarRaws: Int = 1, calendarCols: Int = 1, weekFormat: Bool = false,
          startYear: Int = 0, startMonth: Int = 0, adjustMonth: Int = 0, optionError: Int32 = 0) {
    self.arguments = arguments
    self.calendarRaws = calendarRaws
    self.calendarCols = calendarCols
    self.weekFormat = weekFormat
    self.startYear = startYear
    self.startMonth = startMonth
    self.adjustMonth = adjustMonth
    self.optionError = optionError
  }
  func checkSwitch() {
    var checkString: (value: Int, status: Int32)
    if arguments.count > 4 {
      optionError = 9
    } else if arguments.count != 1 {
      switch arguments[1] {
      case "-y":
        if arguments.count == 4 {
          optionError = 9
          break
        } else if arguments.count == 3 {
          checkString = checkNaturalNumber(target:arguments[2], maxValue:9999, minValue:1800)
          if checkString.status == 0 {
            startYear = checkString.value
          } else {
            optionError = checkString.status
            break
          }
        }
        calendarRaws = 4
        calendarCols = 3
        startMonth = 1
      case "-m", "-3", "+3":
        if arguments.count == 4 {
          checkString = checkNaturalNumber(target:arguments[2], maxValue:12, minValue:1)
          if checkString.status == 0 {
            startMonth = checkString.value
          } else {
            optionError = checkString.status + 3
            break
          }
          checkString = checkNaturalNumber(target:arguments[3], maxValue:9999, minValue:1800)
          if checkString.status == 0 {
            startYear = checkString.value
          } else {
            startMonth = 0
            optionError = checkString.status
            break
          }
        } else if arguments.count == 3 {
          checkString = checkNaturalNumber(target:arguments[2], maxValue:12, minValue:1)
          if checkString.status == 0 {
            startMonth = checkString.value
          } else {
            optionError = checkString.status + 3
            break
          }
        }
        if arguments[1] != "-m" {
          calendarCols = 3
        }
        if arguments[1] == "-3" {
          adjustMonth = -1
        }
      case "-w":
        if arguments.count > 2 {
          optionError = 9
        } else {
          weekFormat = true
        }
      default:
        optionError = 9
      }
    }
  }
  func checkNaturalNumber(target: String, maxValue: Int, minValue: Int) -> (Int, Int32) {
    var value: Int = 0      // OKなら変換値、NGなら-1を返す
    var status: Int32 = 0   // 0:変換および範囲OK、1:上限値越えエラー、2:下限値越えエラー、3:変換エラー
    checkCharacters: for char in target.characters {
      switch char {
      case "0"..."9":
        status = 0
      default:
        status = 3
        value = -1
        break checkCharacters
      }
    }
    if status != 3 {
      value = Int(target)!
      if value > maxValue {
        status = 1
      } else if value < minValue {
        status = 2
      }
    }
    return (value, status)
  }
  func getCalendarFormat() -> (Int, Int, Bool, Int, Int, Int, Int32) {
    self.checkSwitch()
    return (calendarRaws, calendarCols, weekFormat, startYear, startMonth, adjustMonth, optionError)
  }
}

これによりcheckOptionクラスは7種類のエラーを判別できるようになりました。結構ややこしくなってきたので、プロパティ宣言部分にコメント入れておきましょうね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です