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で正規表現を使うサンプルとしてもどうぞ。