文字列を全角/半角に変換する

  • 21
    いいね
  • 2
    コメント

(追記)
@mono0926 さんからコメント頂きまして、Stringの関数であるapplyingTransform(_:reverse) を使うと同様のことができるようです!
iOSなら9+で使えるので、CFString系を扱わずに済むかと思います。
以下コメント原文ママ

"アケマシテオメデトウゴザイマス".applyingTransform(.fullwidthToHalfwidth, reverse: false)
"アケマシテオメデトウゴザイマス".applyingTransform(.fullwidthToHalfwidth, reverse: true)

※以下の内容はSwift2時点のものです。

文章中の"数字"や、ユーザーの入力した文字列中の "数字"で、もし 全角だったら半角に変換をしたかったので、
まとめてみました。

実装

全角→半角になるものとしては、

  • 全角数字(0, 1, ..., 9)
  • 全角英数字(A, B, C, ...)
  • 全角カタカナ (ア, イ, ウ, エ, オ, ...)
  • その他全角/半角で記号が用意されているもの(ハイフンとか)

があるので、正規表現だけだとちょっと大変です。
そこで、 CoreFoundationで提供されている、 CFStringTransform()
を使います。

CFStringTransformの定義
public func CFStringTransform(string: CFMutableString!, _ range: UnsafeMutablePointer<CFRange>, _ transform: CFString!, _ reverse: Bool) -> Bool
  • 第1引数には CFMutableStringを渡します。
    Swiftの場合、 Stringから直接 CFMutableStringに変換するのは大変なので、 NSMutableStringを経由してCFMutableStringに変換します。

  • 第2引数には、rangeを渡しますが、文字列全てを対象にするなら nilで大丈夫です。

  • 第3引数には、変換をかけるタイプを指定します。今回は全角/半角変換をしたいので、 kCFStringTransformFullwidthHalfwidthを指定します。

  • 第4引数には、かける変換を逆変換にするかどうかを指定します。今回の場合、 kCFStringTransformFullwidthHalfwidthを指定するので、

    • falseを渡せばタイプの名前通り、全角から半角に変換します
    • trueを渡せば逆、つまり半角から全角に変換します。

これをふまえて、以下のように実装をします。

extension String {
    private func convertFullWidthToHalfWidth(reverse: Bool) -> String {
        let str = NSMutableString(string: self) as CFMutableString
        CFStringTransform(str, nil, kCFStringTransformFullwidthHalfwidth, reverse)
        return str as String
    }

    var hankaku: String {
        return convertFullWidthToHalfWidth(false)
    }

    var zenkaku: String {
        return convertFullWidthToHalfWidth(true)
    }
}

変換してみる

使用例と変換結果は以下のようになります。

print("000".hankaku) // 000
print("000".zenkaku)   // 000
print("アアア".hankaku) // アアア 
print("AAA".hankaku) // AAA
print("AAA".zenkaku)   // AAA
print("あああ".hankaku) // あああ (そのまま)
print("000".hankaku)   // 000 (半角のものはそのまま)

数字だけ半角/全角変換をしたい

上記のままだと、たとえば、

000 : あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。

という文章に対して、数字だけ半角にしたいと思い半角への変換を実行すると...

let text = "000 : あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。"

print(text.hankaku)
// -> 000 : あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。

と、数字以外のカタカナ、濁点等も半角になってしまいます。 数字だけ変換したい場合は、正規表現で文章中から半角或いは全角数字を抽出して、全角/半角の変換を行う必要があります。

なので、以下のように追記してみます。

extension String {
    // 前略

    private func convertFullWidthToHalfWidthOnlyNumber(fullWidth: Bool) -> String {
        var str = self
        let pattern = fullWidth ? "[0-9]+" : "[0-9]+"
        let regex = try! NSRegularExpression(pattern: pattern, options: [])
        let results = regex.matchesInString(str, options: [], range: NSMakeRange(0, str.characters.count))

        results.reverse().forEach {
            let subStr = (str as NSString).substringWithRange($0.range)
            str = str.stringByReplacingOccurrencesOfString(
                subStr,
                withString: (fullWidth ? subStr.zenkaku : subStr.hankaku))
        }
        return str
    }
    var hankakuOnlyNumber: String {
        return convertFullWidthToHalfWidthOnlyNumber(false)
    }

    var zenkakuOnlyNumber: String {
        return convertFullWidthToHalfWidthOnlyNumber(true)
    }
}

これで、 hankakuOnlyNumberzenkakuOnlyNumberが使えるようになるので、

let text = "000 : あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。"

print(text.hankakuOnlyNumber)
// -> 000 : あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。。

無事、文章中の数字だけ半角に出来ました。
逆に zenkakuOnlyNumberを使えば、

let text2 = "000 : あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。"
print(text2.zenkakuOnlyNumber)
// -> 000 : あのイーハトーヴォのすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られたモリーオ市、郊外のぎらぎらひかる草の波。

と、数字だけ全角にできます。

コード全文

こちらにおいておきます。

おまけ

他にも、ひらがな/カタカナ変換なども用意されているので、同じようにやってみれば、与えた文字列に対して変換をかけられます。
計算型プロパティの名前悩んだのですが、英語でfullWidthとかにするよりはzenkakuのがわかりやすいかなと思ったのでそうしましたが、気に入らなければ英語に直してください〜。