python编写古诗_⽤Python实现古诗词填字游戏(⼀)
利⽤古诗词做填字游戏是⼀项很有趣的活动,通常的填字游戏都是由⼏横⼏竖构成,如下图:
显然,横竖交叉的位置就是两句诗共有的字。那么,问题来了,如何从众多诗⽂中到有共同字的句⼦呢?
这⾥Mr. PosPro⽤Python写了⼀个⼩程序,可以⽣成简单填字游戏(的模型),程序输出的效果如下:
可以看到,程序从全唐诗中到了3句有共同字的句⼦,并以合适的位置完成了排列。?和#代表了交叉处的字,同时在屏幕下⽅给出了最后答案。
下⾯Mr. PosPro教你如何实现这个程序。程序总体上分为三个部分:
(⼀)从TXT⽂件《全唐诗》中提取有⽤信息,并按照我们需要的格式保存到新⽂件中
(⼆)实现⼀个在DOS窗⼝的输出程序,以便在指定位置输出特定⽂字
(三)核⼼部分,抽取诗句,到关联的字,确定每⼀个字的输出位置,并把最后结果交给(⼆)中实现的程序
本次主要讨论第(⼀)部分内容,其它部分的实现请参见后续博客。
想要做⼀个古诗词的填字游戏,⾸先得收集到⾜够多的诗句作为原料库。可以在⽹上搜索“全唐诗TXT“,我选择的版本⼤概有8.2M,内容如图:
这个版本适合直接阅读,但却不适合⽤程序处理,所以⾸先得写⼀段程序,把这四百多万字的⽂本⽂件,转化成我想要形式。对此我是这样设计的:
1>去掉所有⽆关信息,只保留标题,作者,诗⽂内容(标点符号也不要)
2>⼀⾸诗的所有信息都在⼀⾏中表达,从左到右依次为:⾏号,题⽬,作者,诗句全⽂,所有内容Tab隔开。
即,形成如下这个样⼦
下⾯就是具体的程序实现了:
1.读⼊⽂件
i=3200 # PosPro says:在测试时⽆需读取全部信息,可以通过此参数调整读⼊⾏数,加快测试
with open('全唐诗.txt',encoding='gbk',errors="ignore") as f:
for line in f:
line=line.rstrip().lstrip() #去除左右空⽩字符
if i>0:
analyzeText(line)
i-=1
else:
break
python怎么读取txt代码很简单,但有个⼩技巧可以和⼤家分享⼀下:由于⽂件很⼤(超过400万字),在测试阶段如果⼀次性读⼊的话,会很耗时间。这⾥⽤i控制⼀下读⼊的⾏数。毕竟,我们⾸先要验证的是功能的正确。
2. analyzeText函数在⼲什么?
仔细分析《全唐诗》的⽂本,可以发现⼀个特点,即‘卷’和‘【’同时出现的那⼀⾏就是诗⽂的起始,我们应该以此为标志,将程序分为寻下⼀⾸诗,处理标题,处理诗⽂等⼏个阶段,代码如下:(PosPro says: Python的优美之处就在于,程序本⾝和对程序的解释⼏乎是⼀体的,你读懂了代码也就理解了代码。当然,我也会加上⾜够多的注释的。)
INDEXNUM=0
EMPTYLINE=0
STATEFLAG=0
def analyzeText(line):
global INDEXNUM, EMPTYLINE, STATEFLAG
if line=='':
EMPTYLINE+=1
#PosPro says:构成⼀个⽆限循环,只有通过return才能够退出整个函数,读取下⼀⾏
while (True):
if STATEFLAG==0:
#0:始状态,在此状态下若发现某⼀⾏同时包含'卷'和'【',则进⼊诗句标题
if ('卷' in line) and ('【' in line):
STATEFLAG=1
else:
return
#1: 表⽰当前句为标题
if STATEFLAG==1:
INDEXNUM+=1
processTitle(line)
STATEFLAG=2
EMPTYLINE=0
return
#2: 表⽰正在读取诗⽂,但需要特别考虑空⾏和进⼊下⼀⾸诗标题的情况
if STATEFLAG==2:
if EMPTYLINE>2:
processEndPoem()
STATEFLAG=0
EMPTYLINE=0
return
elif ('卷' in line) and ('【' in line):
processEndPoem()
STATEFLAG=1
#PosPro says:此处不return,因为该line还需交由状态1处理
else:
processPoemText(line)
return
3. 分⽽治之,实现对标题、诗⽂,以及结束的分别处理。三个函数⼀起给出:
def processTitle(line):
print (str(INDEXNUM), end='\t') #INDEX就是我⾃⼰做的诗⽂索引
idx1=line.find('【')
idx2=line.find('】')
poemTitle=line[idx1+1:idx2]
author=line[idx2+1:]
print(poemTitle,end='\t')
if author.rstrip()=='':
print ('佚名',end='\t') #发现有些诗句没有注明作者,那我就⾃⼰标⼀下
else:
print(author,end='\t')
def processPoemText(line):
#此时已深⼊到诗句中了,要将各种标点符号删掉,并将每句诗⽂作为list中的⼀项
if not line=='':
#如果要以多个不同字符作为分隔符,就必须⽤
everyLine=re.split(',|。',line)
for l in everyLine:
print (l, end='\t')
def processEndPoem():
print ('') #完成⼀个换⾏
4. 等等,不是说要产⽣⼀个新⽂件么?怎么都是在DOS窗⼝显⽰的啊?
别急,这就是Python另⼀个优雅之处了。在命令⾏敲完⽂件名之后,加上"&",想输出到哪⾥就到哪⾥附:全部代码如下:
## Created by PosPro
## blog.csdn/pospro
import re
i=3200 # PosPro says:在测试时⽆需读取全部信息,可以通过此参数调整读⼊⾏数,加快测试INDEXNUM=0
STATEFLAG=0
def processTitle(line):
print (str(INDEXNUM), end='\t') #INDEX就是我⾃⼰做的诗⽂索引
idx1=line.find('【')
idx2=line.find('】')
poemTitle=line[idx1+1:idx2]
author=line[idx2+1:]
print(poemTitle,end='\t')
if author.rstrip()=='':
print ('佚名',end='\t') #发现有些诗句没有注明作者,那我就⾃⼰标⼀下
else:
print(author,end='\t')
def processPoemText(line):
#此时已深⼊到诗句中了,要将各种标点符号删掉,并将每句诗⽂作为list中的⼀项
if not line=='':
#PosPro says:如果要以多个不同字符作为分隔符,就必须⽤到re模块了
everyLine=re.split(',|。',line)
for l in everyLine:
print (l, end='\t')
def processEndPoem():
print ('') #完成⼀个换⾏
def analyzeText(line):
global INDEXNUM, EMPTYLINE, STATEFLAG
if line=='':
EMPTYLINE+=1
#PosPro says:构成⼀个⽆限循环,只有通过return才能够退出整个函数,读取下⼀⾏while (True):
if STATEFLAG==0:
#0:始状态,在此状态下若发现某⼀⾏同时包含'卷'和'【',则进⼊诗句标题
if ('卷' in line) and ('【' in line):
STATEFLAG=1
else:
return
#1: 表⽰当前句为标题
if STATEFLAG==1:
INDEXNUM+=1
processTitle(line)
STATEFLAG=2
EMPTYLINE=0
return
#2: 表⽰正在读取诗⽂,但需要特别考虑空⾏和进⼊下⼀⾸诗标题的情况if STATEFLAG==2:
if EMPTYLINE>2:
processEndPoem()
STATEFLAG=0
EMPTYLINE=0
return
elif ('卷' in line) and ('【' in line):
processEndPoem()
STATEFLAG=1
EMPTYLINE=0
#PosPro says:此处不return,因为该line还需交由状态1处理
else:
processPoemText(line)
return
with open('全唐诗.txt',encoding='gbk',errors="ignore") as f:
for line in f:
#去除左右空⽩字符
line=line.rstrip().lstrip()
if i>0:
analyzeText(line)
i-=1
else:
break