用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
       代碼很簡單,但有個小技巧可以和大家分享一下:由於文件很大(超過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







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章