場景
目前正在做閱讀器聽書功能,集成了百度語音后,每次要給語音SDK傳入一句話,然后得到語音。但是有長度限制。為了語音流暢,需要將一章文字按句分組,之后管理一個(gè)隊(duì)列數(shù)組,用于語音功能。
如何給一段話分句
實(shí)踐一:使用 Sting的 components(separatedBy: ) 方法
其實(shí)最開始是想通過正則表達(dá)式分組,最終未能實(shí)現(xiàn)。之后使用該方法。其實(shí)分組不僅僅可以傳入單個(gè)字符,也能傳入一個(gè)字符組:
components(separatedBy: <#T##StringProtocol#>)
components(separatedBy: <#T##CharacterSet#>)
這個(gè)也是我最初采用的方法。但是之后發(fā)現(xiàn)存在比較嚴(yán)重的問題,并且并未解決。
存在問題:首先,分組后的語句是不包含作為分隔符的標(biāo)點(diǎn)符號的,這樣對于閱讀器切頁時(shí)每頁的 Range 就產(chǎn)生了影響,沒法精確判斷讀到該頁末尾時(shí)翻頁、翻章。其次,閱讀文字時(shí)需要字體高亮,但是通過該方法分組后的語句,對于作為分隔符的標(biāo)點(diǎn)符號,如果緊鄰其他符號,其他符號為氛圍下一句,最典型的就是 。” ,分組后,下引號會分到下一句,明顯很難看。
實(shí)現(xiàn)代碼如下:
let str = "我說:“這是一句話?!倍野凑宅F(xiàn)在的情況來看,這個(gè)奧術(shù)還是特別加強(qiáng)了穩(wěn)固性和構(gòu)成速度!頃刻之間就要爆發(fā)了。“混蛋……居然是這樣的陷阱…”女法師臉上浮現(xiàn)出了悲憤,無奈,絕望。"
let matchs = str.components(separatedBy: CharacterSet.init(charactersIn: " 。?。?;\n "))
matchs.forEach { (s) in
print("?? --- \(s)")
}
控制臺輸出:
?? --- 我說:“這是一句話
?? --- ”而且按照現(xiàn)在的情況來看,這個(gè)奧術(shù)還是特別加強(qiáng)了穩(wěn)固性和構(gòu)成速度
?? --- 頃刻之間就要爆發(fā)了
?? --- “混蛋……居然是這樣的陷阱…”女法師臉上浮現(xiàn)出了悲憤,無奈,絕望
?? ---
存在問題顯而易見,尤其是需要這句話高亮?xí)r,問題更明顯。由此,查資料時(shí)找到了第二種方法。
實(shí)踐二:采用原生 API 接口 CFStringTokenizer
CFStringTokenizer功能很強(qiáng)大,之前也從未使用過,這里也只是通過查詢一些博客,采用了自己需要的一點(diǎn)功能。
let str = "我說:“這是一句話。”而且按照現(xiàn)在的情況來看,這個(gè)奧術(shù)還是特別加強(qiáng)了穩(wěn)固性和構(gòu)成速度!頃刻之間就要爆發(fā)了?!盎斓啊尤皇沁@樣的陷阱…”女法師臉上浮現(xiàn)出了悲憤,無奈,絕望。"
let ref = CFStringTokenizerCreate(nil, str as CFString, CFRangeMake(0, str.lenght), kCFStringTokenizerUnitSentence, nil)
CFStringTokenizerAdvanceToNextToken(ref)
var range: CFRange
range = CFStringTokenizerGetCurrentTokenRange(ref)
// 循環(huán)
var sentence = ""
var sentences = [String]()
while range.length > 0 {
sentence = str.substring(NSMakeRange(range.location, range.length))
sentences.append(sentence)
CFStringTokenizerAdvanceToNextToken(ref)
range = CFStringTokenizerGetCurrentTokenRange(ref)
print("?? \(sentence)")
}
控制臺輸出:
?? 我說:“這是一句話。”
?? 而且按照現(xiàn)在的情況來看,這個(gè)奧術(shù)還是特別加強(qiáng)了穩(wěn)固性和構(gòu)成速度!
?? 頃刻之間就要爆發(fā)了?!??? 混蛋……居然是這樣的陷阱…”女法師臉上浮現(xiàn)出了悲憤,無奈,絕望。
是不是符合預(yù)期的結(jié)果直接就出來了。。暫時(shí)效果如下圖:

如果修改屬性 kCFStringTokenizerUnitSentence 為 kCFStringTokenizerUnitWord ,就是按照關(guān)鍵詞分組了。其實(shí)這個(gè)屬性還有其他各種設(shè)置,具體使用時(shí)可以根據(jù)需要修改。
另外,關(guān)鍵詞分組效果類似:
?? 我
?? 說
?? 這
?? 是
?? 一
?? 句
?? 話
?? 而且
?? 按照
?? 現(xiàn)在
?? 的
如何暫停倒計(jì)時(shí)
這個(gè)也是剛剛用到的一個(gè)方法。一旦 timer.invalidate() 后,并不能在開啟了。下面是一個(gè)簡單的計(jì)時(shí)器實(shí)現(xiàn):
// MARK: - 計(jì)時(shí)器
@IBOutlet weak var timeView: UIView!
@IBOutlet weak var timeLabel: UILabel!
private var countdownTimer: Timer?
private var durtion: Int = 0
// 開始
@IBAction func startTimerAction(_ sender: UIButton) {
if countdownTimer == nil {
countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
}
durtion = 0
}
// 暫停
@IBAction func pauseTimerAction(_ sender: UIButton) {
countdownTimer?.fireDate = Date.distantFuture
}
// 繼續(xù)
@IBAction func resumeTimerAction(_ sender: UIButton) {
countdownTimer?.fireDate = Date.distantPast
}
// 停止
@IBAction func stopTimerAction(_ sender: UIButton) {
countdownTimer?.invalidate()
countdownTimer = nil
}
// 計(jì)時(shí)
@objc private func countdown() {
durtion += 1
let delaySeconds = 30 * 60 - durtion
let minutes = delaySeconds / 60
let seconds = delaySeconds % 60
let timeStr = String(format: "%02d:%02d", arguments: [minutes, seconds])
timeLabel.text = timeStr
}