用Python实现古诗词填字游戏(一)

             利用古诗词做填字游戏是一项很有趣的活动,通常的填字游戏都是由几横几竖构成,如下图:
用Python实现古诗词填字游戏(一)_第1张图片
       显然,横竖交叉的位置就是两句诗共有的字。那么,问题来了,如何从众多诗文中找到有共同字的句子呢?

       这里Mr. PosPro用Python写了一个小程序,可以生成简单填字游戏(的模型),程序输出的效果如下:
用Python实现古诗词填字游戏(一)_第2张图片
       可以看到,程序从全唐诗中找到了3句有共同字的句子,并以合适的位置完成了排列。?和#代表了交叉处的字,同时在屏幕下方给出了最后答案。

       下面Mr. PosPro教你如何实现这个程序。程序总体上分为三个部分:
(一)从TXT文件《全唐诗》中提取有用信息,并按照我们需要的格式保存到新文件中
(二)实现一个在DOS窗口的输出程序,以便在指定位置输出特定文字
(三)核心部分,抽取诗句,找到关联的字,确定每一个字的输出位置,并把最后结果交给(二)中实现的程序

        本次主要讨论第(一)部分内容,其它部分的实现请参见后续博客。

       想要做一个古诗词的填字游戏,首先得收集到足够多的诗句作为原料库。可以在网上搜索“全唐诗TXT“,我选择的版本大概有8.2M,内容如图:
用Python实现古诗词填字游戏(一)_第3张图片
            这个版本适合直接阅读,但却不适合用程序处理,所以首先得写一段程序,把这四百多万字的文本文件,转化成我想要形式。对此我是这样设计的:
1>去掉所有无关信息,只保留标题,作者,诗文内容(标点符号也不要)
2>一首诗的所有信息都在一行中表达,从左到右依次为:行号,题目,作者,诗句全文,所有内容Tab隔开。
       即,形成如下这个样子
用Python实现古诗词填字游戏(一)_第4张图片

下面就是具体的程序实现了:
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
       代码很简单,但有个小技巧可以和大家分享一下:由于文件很大(超过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
				EMPTYLINE=0
				#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另一个优雅之处了。在命令行敲完文件名之后,加上">1.txt",想输出到哪里就到哪里



附:全部代码如下:
## Created by PosPro
## http://blog.csdn.net/pospro

import re
i=3200  # PosPro says:在测试时无需读取全部信息,可以通过此参数调整读入行数,加快测试

INDEXNUM=0
EMPTYLINE=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







你可能感兴趣的:(python,编程实例)