Swiftで青空文庫のルビをパースする

青空文庫のルビ表記をパースするサンプルを作ってみました。

青空文庫のルビはこんな感じで定義されてます。
http://www.aozora.gr.jp/KOSAKU/MANUAL_2.html

本来はリンクの通り、いろいろと細かな仕様があるのですが、今回はプログラムで「ルビ付き文字列を定義する」ということだけを狙ってシンプルな仕様で実装してみます。
具体的には↓のフォーマットだけを対象とします。

|漢字《よみかた》

例えばこんな感じですね。

「てめえらの|血《ち》は|何色《なにいろ》だーっ!!」

先に使い方ですが

let text = "てめえらの|血《ち》は|何色《なにいろ》だーっ!!"
let parser = AozoraRubyParser(text: text)
if let result = parser.parse() {
	for r in result {
		print("\(r.text) \( (r.ruby != nil) ? "[" + r.ruby! + "]" : "" )")
	}
}

こんな感じで出力されます

てめえらの
血 [ち]

何色 [なにいろ]
だーっ!!

class AozoraRubyParser {
  
  class Result {
    let text:String
    let ruby:String?
    init(text:String,ruby:String?) {
      self.text = text
      self.ruby = ruby
    }
  }
  
  
  let text:String;  
  
  init(text:String) {
    self.text = text;
  }
    
  func parse() -> [Result]?{    
    
    var result:[Result] = []
    do {
  
      var position = 0;
      let nsText = text as NSString
      let totalLength = nsText.length
      
      let re = try NSRegularExpression(pattern: "|(.+?)《(.+?)》", options: [])
  
      let matches = re.matches(in: text, options: [], range: NSRange(location: 0, length: totalLength))
      for match in matches {
        let matchRange = match.range;
        
        // 現在の位置からrangeの先頭が離れていたら、その間の文字を抽出する
        if position < matchRange.location {
          let subRange = NSRange(location: position, length: matchRange.location - position)
          result.append(Result(text: nsText.substring(with:subRange), ruby: nil))
        }
        position = matchRange.location + matchRange.length;
	
        // 文字と読み仮名を抽出
        let str = nsText.substring(with: match.rangeAt(1))
        let ruby = nsText.substring(with: match.rangeAt(2))
        result.append(Result(text: str, ruby: ruby))
        
      }
      
      // 文末の残りを追加
      if totalLength > position {
        let subRange = NSRange(location: position, length: totalLength - position)
        result.append(Result(text: nsText.substring(with:subRange), ruby: nil))
      }
      
      
    }catch let err  {      
      log.error("\(err)")
      return nil;
    }
  
    return result
  }

}

Swiftで正規表現を使うサンプルとしてもどうぞ。