pull/464/head
Robot 4 years ago
commit 74370d5d1f
  1. 119
      app/src/main/java/io/legado/app/help/ContentHelp.kt

@ -1,6 +1,7 @@
package io.legado.app.help
import java.util.*
import java.util.regex.Pattern
import kotlin.math.max
import kotlin.math.min
@ -30,6 +31,7 @@ object ContentHelp {
} else {
content1
}
val dict = makeDict(content2)
var p = content2
.replace(""".toRegex(), "")
.replace("[::]['\"‘”“]+".toRegex(), ":“")
@ -68,9 +70,7 @@ object ContentHelp {
buffer = StringBuffer((content1.length * 1.15).toInt())
for (s in p) {
buffer.append("\n")
buffer.append(
findNewLines(s)
)
buffer.append(findNewLines(s, dict))
}
buffer = reduceLength(buffer)
content1 = ("$chapterName\n\n" + buffer.toString() // 处理章节头部空格和换行
@ -178,18 +178,18 @@ object ContentHelp {
}
// 对内容重新划分段落.输入参数str已经使用换行符预分割
private fun findNewLines(str: String): String {
private fun findNewLines(str: String, dict: List<String>): String {
val string = StringBuffer(str)
// 标记string中每个引号的位置.特别的,用引号进行列举时视为只有一对引号。 如:“锅”、“碗”视为“锅、碗”,从而避免误断句。
val arrayQuote: MutableList<Int> = ArrayList()
// 标记插入换行符的位置,int为插入位置(str的char下标)
var insN = ArrayList<Int>()
// mod[i]标记str的每一段处于引号内还是引号外。范围: str.substring( array_quote.get(i), array_quote.get(i+1) )的状态。
// 长度:array_quote.size(),但是初始化时未预估占用的长度,用空间换时间
// 0未知,正数引号内,负数引号外。
// 如果相邻的两个标记都为+1,那么需要增加1个引号。
// 引号内不进行断句
//mod[i]标记str的每一段处于引号内还是引号外。范围: str.substring( array_quote.get(i), array_quote.get(i+1) )的状态。
//长度:array_quote.size(),但是初始化时未预估占用的长度,用空间换时间
//0未知,正数引号内,负数引号外。
//如果相邻的两个标记都为+1,那么需要增加1个引号。
//引号内不进行断句
val mod = IntArray(str.length)
var waitClose = false
for (i in str.indices) {
@ -201,7 +201,16 @@ object ContentHelp {
if (size > 0) {
val quotePre = arrayQuote[size - 1]
if (i - quotePre == 2) {
if (match(",、,/", str[i - 1])) {
var remove = false
if (waitClose) {
if (match(",,、/", str[i - 1])) {
// 考虑出现“和”这种特殊情况
remove = true
}
} else if (match(",,、/和与或", str[i - 1])) {
remove = true
}
if (remove) {
string.setCharAt(i, '“')
string.setCharAt(i - 2, '”')
arrayQuote.removeAt(size - 1)
@ -232,8 +241,10 @@ object ContentHelp {
}
}
}
// if(char_b2=='.' || char_b2=='。')
if (match(MARK_SENTENCES_END_P, charB2)) insN.add(p - 1) else {
//if(char_b2=='.' || char_b2=='。')
if (match(MARK_SENTENCES_END_P, charB2)) {
insN.add(p - 1)
} else if (!match("", charB2)) {
val lastEnd = seekLast(str, MARK_SENTENCES_END, i, lastQuote)
if (lastEnd > 0) insN.add(lastEnd) else insN.add(lastQuote)
}
@ -258,16 +269,15 @@ object ContentHelp {
val size = arrayQuote.size
// 标记循环状态,此位置前的引号是否已经配对
//标记循环状态,此位置前的引号是否已经配对
var opend = false
if (size > 0) {
// 第1次遍历array_quote,令其元素的值不为0
//第1次遍历array_quote,令其元素的值不为0
for (i in 0 until size) {
if (mod[i] > 0) {
opend = true
} else if (mod[i] < 0) {
// 连续2个反引号表明存在冲突,强制把前一个设为正引号
//连续2个反引号表明存在冲突,强制把前一个设为正引号
if (!opend) {
if (i > 0) mod[i] = 3
}
@ -280,7 +290,7 @@ object ContentHelp {
// 修正,断尾必须封闭引号
if (opend) {
if (arrayQuote[size - 1] - string.length > -3) {
// if((match(MARK_QUOTATION,string.charAt(string.length()-1)) || match(MARK_QUOTATION,string.charAt(string.length()-2)))){
//if((match(MARK_QUOTATION,string.charAt(string.length()-1)) || match(MARK_QUOTATION,string.charAt(string.length()-2)))){
if (size > 1) mod[size - 2] = 4
// 0<=i<size,故无需判断size>=1
mod[size - 1] = -4
@ -318,9 +328,39 @@ object ContentHelp {
}
}
// 第3次循环,匹配并插入换行。
// "xxxx" xxxx。\n xxx“xxxx”
// 未实现
//第3次循环,匹配并插入换行。
//"xxxx" xxxx。\n xxx“xxxx”
//未实现
// 使用字典验证ins_n , 避免插入不必要的换行。
// 由于目前没有插入、的列表,无法解决 “xx”、“xx”“xx” 被插入换行的问题
val insN1 = ArrayList<Int>()
for (i in insN) {
if (match("\"'”“", string[i])) {
val start: Int = seekLast(
str,
"\"'”“",
i - 1,
i - WORD_MAX_LENGTH
)
if (start > 0) {
val word = str.substring(start + 1, i)
if (dict.contains(word)) {
//System.out.println("使用字典验证 跳过\tins_n=" + i + " word=" + word);
//引号内如果是字典词条,后方不插入换行符(前方不需要优化)
continue
} else {
//System.out.println("使用字典验证 插入\tins_n=" + i + " word=" + word);
if (match("的地得", str[start])) {
//xx的“xx”,后方不插入换行符(前方不需要优化)
continue
}
}
}
}
insN1.add(i)
}
insN = insN1
// 随机在句末插入换行符
insN = ArrayList(HashSet(insN))
@ -471,6 +511,34 @@ object ContentHelp {
return buffer.toString()
}
/**
* 从字符串提取引号包围,且不止出现一次的内容为字典
*
* @param str
* @return 词条列表
*/
private fun makeDict(str: String): List<String> {
// 引号中间不包含任何标点
val patten = Pattern.compile(
"""
(?<=["'”“])([^
\p{P}]{1,${WORD_MAX_LENGTH}})(?=["'”“])
""".trimIndent()
)
//Pattern patten = Pattern.compile("(?<=[\"'”“])([^\n\"'”“]{1,16})(?=[\"'”“])");
val matcher = patten.matcher(str)
val cache: MutableList<String> = ArrayList()
val dict: MutableList<String> = ArrayList()
while (matcher.find()) {
val word = matcher.group()
if (cache.contains(word)) {
if (!dict.contains(word)) dict.add(word)
} else cache.add(word)
}
return dict
}
/**
* 计算匹配到字典的每个字符的位置
*
@ -510,14 +578,14 @@ object ContentHelp {
*
* @param str 数据字符串
* @param key 字典字符串
* @param from 从哪个字符开始匹配默认0
* @param to 匹配到哪个字符不包含此字符默认匹配到最末位
* @param from 从哪个字符开始匹配默认最末位
* @param to 匹配到哪个字符不包含此字符默认0
* @return 位置正向计算)
*/
private fun seekLast(str: String, key: String, from: Int, to: Int): Int {
if (str.length - from < 1) return -1
var i = 0
if (from > i) i = from
var i = str.lastIndex
if (from < i && i > 0) i = from
var t = 0
if (to > 0) t = to
var c: Char
@ -619,6 +687,9 @@ object ContentHelp {
private const val MARK_QUOTATION = "\"“”"
private val PARAGRAPH_DIAGLOG = "^[\"”“][^\"”“]+[\"”“]$".toRegex()
// 限制字典的长度
private const val WORD_MAX_LENGTH = 16
private fun match(rule: String, chr: Char): Boolean {
return rule.indexOf(chr) != -1
}

Loading…
Cancel
Save