python函数格式化输出唐诗《锦瑟》_Python文本分析案例:近体诗格律分析

作者:长行

时间:2020.05.26

Github原文:Week-03/Example-0301

在这个案例中,我们将要实现近体诗格律的分析。具体的,我们从如下角度分析近体诗的格律:

诗句数量、诗句字数是否符合近体诗的要求,即是否为五绝、七绝、五律、七律中的一种(暂不考虑排律、六言的情况)

是否押了平声韵,所押的韵脚是什么平水韵部(暂不考虑首句押韵的情况)

诗句是否有拗句,是否存在孤平和拗救的情况

诗文是否符合对黏的要求

如果当时该诗不符合第1个或第2个要求,则不再分析;如果符合第1个和第2个要求,则以如下方式分析该诗的格律。

《首春》 李世民

寒随穷律变,春逐鸟声开。

平平平仄仄 平仄仄平平(十灰) —— 仄仄脚正格 平平脚正格

初风飘带柳,晚雪间花梅。

平平平仄仄 仄仄中平平(十灰) —— 【失黏】仄仄脚正格 平平脚正格

碧林青旧竹,绿沼翠新苔。

仄平平仄仄 仄仄仄平平(十灰) —— 【失黏】仄仄脚正格 平平脚正格

芝田初雁去,绮树巧莺来。

平平平仄中 仄中中平平(十灰) —— 【失黏】仄仄脚正格 平平脚正格

下面我们开始实现以上功能。

首先,我们读取案例0201-全唐诗文本整理案例中已经整理好的全唐诗文本进行遍历。

with open("全唐诗.json", encoding="UTF-8") as file:

poem_json = json.loads(file.read())

平水韵表详见“全唐诗.json”

第1个要求

检查诗句数量

近体诗在诗句数量上主要绝句、律诗和排律,绝句(律绝)由四句组成,律诗由八句组成,排律则超过八句,当前只考虑绝句和律诗的情况;在具体实现上,我们可以通过正则表达式,依据标点符号来拆分诗文中的各个句子,并统计诗句的总数是否为4或8。

sentence_list = [sentence for sentence in re.split("[,。?!]", content) if sentence != ""] # 生成句子列表

if len(sentence_list) != 4 and len(sentence_list) != 8:

print("[古体诗]诗句数量:", len(sentence_list))

检查诗句字数

近体诗在诗句字数上通常包括五言和七言两种(六言律诗传世极少,暂不考虑);在具体实现上,我们使用列表生成式和内置函数all判断诗中句子是否均为五言或七言。

if not all([len(sentence) == 5 or len(sentence) == 7 for sentence in sentence_list]):

print("[古体诗]诗句并非五言或七言", [len(sentence) for sentence in sentence_list])

诗句平仄标注

第2个、第3个、第4个要求的分析都需要我们先标注诗句的平仄,即完成类似于如下方式的标注。

寒随穷律变,春逐鸟声开。

平平平仄仄 平仄仄平平

我们使用查平水韵表的方式,来判断诗句中每个字的平仄并标注。其中阴平和阳平均属于平声,标注为“平”;上声、去声、入声均属于仄声,标注为“仄”。若某字不存在于平水韵表中,或同时存在于多个不同平仄的韵部中,均认为该字平仄无法判断,标注为“中”;在后续的分析中,不考虑这些无法判断平仄的字的影响。

在具体实现上,包括如下步骤:

导入平水韵表并将其整理为字典;其中key为字,value为所有包含该字的韵部的名称及音调的列表;

使用导入平水韵表的字典,实现判断单个字平仄的函数;

使用判断单个字平仄的函数,实现诗句平仄的标注。

导入平水韵表

def load_rhythm_list():

"""

载入平水韵表并转换为dict形式

"""

with open("平水韵表.txt", encoding="UTF-8") as file:

rhythm_lines = file.readlines()

rhythm_dict = dict()

for rhythm_line in rhythm_lines:

rhythm_name = re.search(".*(?=[平上去入]声:)", rhythm_line).group() # 读取韵部名称

rhythm_tune = re.search("[平上去入](?=声:)", rhythm_line).group() # 读取韵部声调

rhythm_characters = re.sub(".*[平上去入]声:", "", rhythm_line) # 获取韵部包含的所有文字

for character in rhythm_characters:

if character not in rhythm_dict:

rhythm_dict[character] = list()

rhythm_dict[character].append([rhythm_name, rhythm_tune])

return rhythm_dict

平水韵表详见“平水韵表.txt”

实现计算单个字的平仄

def get_tone(rhythm_list):

"""

依据字所在的韵部,返回当前字的平仄(若当前字为无法确定平仄的多音字,则返回“中”)

:return: 平/仄/中

"""

tone_set = set()

for rhythm_item in rhythm_list:

tone_set.add(rhythm_item[1])

if len(tone_set) == 1: # 若当前字不是多音字或是平仄相同的多音字

return list(tone_set)[0]

else:

return "中"

实现计算诗句中各字的平仄情况

sentence_tone = list()

for sentence in sentence_list:

sentence_tone.append([get_tone(character) for character in sentence])

第2个要求

在近体诗中,我们将奇数句称为出句,将偶数句称为对句,一组出句和对句称为一联。通常来说,近体诗的对句需要押相同的平声韵,首句如押韵可押邻韵“借韵”。因此,我们检查诗句是否押平声韵,只需要检查所有偶数句是否押平声韵即可。在具体的实现上,我们使用列表生成式和内置函数all判断诗文是否押了平声韵。

if not all([sentence_tone[i][-1] in ["平", "中"] for i in range(len(sentence_tone)) if i % 2 == 1]):

print("诗文未押韵或押仄声韵")

第3个要求

“拗句”是指诗句不符合格律。诗句的格律大体上可以用“一三五不论,二四六分明“来概括,包括多种正格、变格,情况十分复杂。在具体的实现上,我们使用的方法,利用正则表达式来匹配所有正格和变格,由此判断诗句是否为拗句。

def inspect_sentence_tone(sentence_tone):

"""

判断诗句是否为拗句,是否存在孤平拗救的情况

:return: 诗句是否正确, 诗句的正格(用于判断对黏), 诗句情况详细说明

"""

if re.match("[平仄中]?[平中]?[平仄中][仄中][平中][平中][仄中]", sentence_tone): # (仄)仄平平仄

return True, "仄仄平平仄", "平仄脚正格"

elif re.match("[平仄中]?[仄中]?[平仄中][平中][平中][仄中][仄中]", sentence_tone): # (平)平平仄仄

return True, "平平平仄仄", "仄仄脚正格"

elif re.match("[平仄中]?[平中]?[平仄中][仄中][仄中][平中][平中]", sentence_tone): # (仄)仄仄平平

return True, "仄仄仄平平", "平平脚正格"

elif re.match("[平仄中]?[仄中]?[平中][平中][仄中][仄中][平中]", sentence_tone): # 平平仄仄平

return True, "平平仄仄平", "仄平脚正格"

elif re.match("[平仄中]?[平中]?[平仄中][仄中][仄中][平中][仄中]", sentence_tone): # (仄)仄仄平仄

return True, "仄仄平平仄", "平仄脚变格(半拗可救可不救,若救对句五言第第三字、七言第五字用平)"

elif re.match("[平仄中]?[平中]?[平仄中][仄中][平仄中][仄中][仄中]", sentence_tone): # (仄)仄(平)仄仄

return True, "仄仄平平仄", "平仄脚变格(大拗必救,对句五言第第三字、七言第五字用平)"

elif re.match("[平仄中]?[仄中]?[平中][平中][仄中][平仄中][仄中]", sentence_tone): # 平平仄(平)仄

return True, "平平平仄仄", "仄仄脚变格(五言第一字、七言第三字必平)"

elif re.match("[平仄中]?[仄中]?[平仄中][仄中][平中][平中][平中]", sentence_tone): # (仄)仄平平平

return True, "仄仄仄平平", "平平脚变格(极其罕见)"

elif re.match("[平仄中]?[仄中]?[仄中][平中][平中][仄中][平中]", sentence_tone): # 仄平平仄平

return True, "平平仄仄平", "仄平脚变格(孤平拗救,五言第一字、七言第三字必平)"

elif re.match("[平仄中]?[仄中]?[平中][平中][平中][仄中][平中]", sentence_tone): # 平平平仄平

return True, "平平仄仄平", "仄平脚变格(出句拗对句救,五言第三字、七言第五字用平)"

else:

return False, "", "拗句"

第4个要求

诗句的对黏,是指相邻诗句之间的平仄关系。简单来说,同一联中的出句和对句中的第2字、第4字、第6字的平仄应不同,即“对”,若平仄相同则称为“失对”;上一联的对句和下一联的出句的第2字、第4字、第6字的平仄应相同,即“黏”,若平仄不同则称为“失黏”。在判断过程中,如果诗句是变格,则以其原始正格的平仄为准。

判断诗句“对”的情况

def is_tone_differ(tone_1, tone_2):

"""

判断两个字平仄是否不同

"""

if (tone_1 == "仄" or tone_1 == "中") and (tone_2 == "平" or tone_2 == "中"):

return True

elif (tone_1 == "平" or tone_1 == "中") and (tone_2 == "仄" or tone_2 == "中"):

return True

else:

return False

def inspect_corresponding(first_type, second_type):

"""

判断句子的对是否正确

:param first_type: 出句的正格

:param second_type: 对句的正格

:return: """

if len(first_type) != len(second_type):

return False

return is_tone_differ(first_type[-2], second_type[-2]) and is_tone_differ(first_type[-1], second_type[-1])

判断诗句“黏”的情况

def is_tone_same(tone_1, tone_2):

"""

判断两个字平仄是否相同

"""

if (tone_1 == "仄" or tone_1 == "中") and (tone_2 == "仄" or tone_2 == "中"):

return True

elif (tone_1 == "平" or tone_1 == "中") and (tone_2 == "平" or tone_2 == "中"):

return True

else:

return False

def inspect_sticky(last_second_type, this_first_type):

"""

判断句子的黏是否正确

:param last_second_type: 前句对句的正格

:param this_first_type: 当前出句的正格

:return: """

if len(last_second_type) != len(this_first_type):

return False

return is_tone_same(last_second_type[-2], this_first_type[-2])

整理并输出结果

在以上部分,我们已经可以实现所有的需求目标了,下面通过调用以上代码以实现诗文格律分析。

def poem_analyse(title, author, content):

sentences = [sentence for sentence in re.split("[,。?!]", content) if sentence != ""]

punctuations = re.findall("[,。?!]", content)

# 检查诗文的句子数量是否为绝句或律诗

if len(sentences) != 4 and len(sentences) != 8:

print("《" + title + "》", author, "诗句句数不是绝句或律诗")

return False

# 检查诗文中句子的字数是否为五言或七言

if not all([len(sentence) == 5 or len(sentence) == 7 for sentence in sentences]):

print("《" + title + "》", author, "诗文中句子的字数不是五言或七言")

return False

# 计算诗句中每个字的平仄情况

sentence_tone_list = list()

for sentence in sentences:

sentence_tone_list.append("".join([get_tone(character) for character in sentence]))

# 判断是否押平声韵

if not all([sentence_tone_list[i][-1] in ["平", "中"] for i in range(len(sentences)) if i % 2 == 1]):

print("《" + title + "》", author, "诗文没有押韵或押仄声韵")

return False

print("《" + title + "》", author)

last_second_type = ""

for i in range(int(len(sentences) / 2)):

first_sentence = sentences[2 * i + 0] # 出句内容

second_sentence = sentences[2 * i + 1] # 对句内容

first_tone = sentence_tone_list[2 * i + 0] # 出句的平仄

second_tone = sentence_tone_list[2 * i + 1] # 对句的平仄

second_rhythm = "(" + get_rhythm(second_sentence[-1]) + ")" # 对句的韵脚

first_correct, first_type, first_explanation = inspect_sentence_tone(first_tone)

second_correct, second_type, second_explanation = inspect_sentence_tone(second_tone)

other_analysis = ""

if first_correct and second_correct:

if not inspect_corresponding(first_type, second_type): # 判断是否对

other_analysis += "【失对】"

if last_second_type is not None and inspect_sticky(last_second_type, first_type): # 判断是否黏

other_analysis += "【失黏】"

last_second_type = second_type

output_sentence = first_sentence + punctuations[2 * i + 0] + second_sentence + punctuations[2 * i + 1] # 第一行输出

output_analysis = first_tone + " " + second_tone + second_rhythm # 第二行输出

output_analysis += " —— " + other_analysis + first_explanation + " " + second_explanation

print(output_sentence)

print(output_analysis)

return True

if __name__ == "__main__":

with open("全唐诗.json", encoding="UTF-8") as file: # 载入整理完成的全唐诗文本语料

poem_json = json.loads(file.read())

for poem_item in poem_json["data"]:

if poem_analyse(poem_item["title"], poem_item["author"], poem_item["content"].replace("\n", "")):

print("点击回车继续...")

input()

运行结果实现了需求:

......

《春日望海》 李世民 诗句句数不是绝句或律诗

《临洛水》 李世民

春蒐驰骏骨,总辔俯长河。

平平平仄仄 中仄仄中平(五歌) —— 仄仄脚正格 平平脚正格

霞处流萦锦,风前漾卷罗。

平仄平平仄 平平仄中平(五歌) —— 【失黏】平仄脚正格 仄平脚正格

水花翻照树,堤兰倒插波。

仄平平仄仄 平平仄仄平(五歌) —— 【失对】【失黏】仄仄脚正格 仄平脚正格

岂必汾阴曲,秋云发棹歌。

仄仄平平仄 平平仄仄平(五歌) —— 平仄脚正格 仄平脚正格

点击回车继续...

完整源代码详见诗词格律分析(面对过程).py和诗词格律分析(面对对象).py

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值