You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

247 lines
9.4 KiB

import os
import re # find and replace
import shutil # move tmp file for overwrite
cantoDir = os.path.abspath(os.getcwd())
def getXMLFileName(xmlCantoFile):
xmlCanto = xmlCantoFile.split('.')[0] + '.xml'
return xmlCanto
def addWordTags(cantoFile):
"""
- [x] Parse filename to create xml file
- [x] Open file as one huge string (keep track of newlines)
- [x] Replace every space with a dummy char not used in the text, like `~`
- [ ] Traverse the big file-string from beginning to end
- [x] Define a "word-chars" group: letters, apostrofes
- [x] If you encounter a char that's a word-char
put a `<w>` in front of it and move to the next.
- [x] If the next char is also a word-char, move on
to the next because we're still in a word.
- [x] If the next char is not a word-char,
put a `</w>` in front of it and move to the next.
- [x] Replace all `~` with spaces and get back the newlines
- [x] Write to XML file
"""
xmlCanto = getXMLFileName(cantoFile)
with open(xmlCanto, 'w') as xmlCanto:
with open(cantoFile, 'r') as txtCanto:
originalText = txtCanto.read()
# Prepare canto for traversing
plainText = traversePreparation(originalText, '\n', '+', False)
plainText = traversePreparation(plainText, ' ', '~', False)
# Define variables
wordChars = '[a-zA-ZÀ-ÿ0-9‘’]'
openWord = '<w>'
closeWord = '</w>'
insideWord = False
# Traverse canto
for char in plainText:
if re.match(wordChars, char):
if insideWord == False:
xmlCanto.write(openWord)
xmlCanto.write(char)
insideWord = True
else:
xmlCanto.write(char)
else:
if char == '~':
if insideWord == True:
xmlCanto.write(closeWord + ' ')
insideWord = False
else:
xmlCanto.write(' ')
elif char == '+':
if insideWord == True:
xmlCanto.write(closeWord + '\n') # line ends with word
insideWord = False
else:
xmlCanto.write('\n') # line ends with punctuation
elif insideWord == True:
xmlCanto.write(closeWord + char) # punctuation right after to word
insideWord = False
else:
xmlCanto.write(char) # punctuation after punctuation
txtCanto.close()
xmlCanto.close()
def traversePreparation(cantoText, find, replace, negate):
"""
Substitute find with replace.
If negate is True, substitute replace with find.
"""
if negate == False:
replacedText = cantoText.replace(find, replace)
else:
replacedText = cantoText.replace(replace, find)
return replacedText
def addLineTags(xmlCantoFile):
"""
add <l l_num="n"> to the beginning of each line number n
add </l> to the end of each line
"""
xmlCanto = getXMLFileName(xmlCantoFile)
tmpFile = '.' + xmlCantoFile + '.tmp'
with open(xmlCanto, 'r') as xmlCanto:
with open(tmpFile, 'w') as tmpCanto:
i = 1
for line in xmlCanto:
# replace '\n' in line with ' </l>\n'
newLine = re.sub('\n', ' </l>\n', line)
newLine = '<l l_num="' + str(i) + '"> ' + newLine
i += 1
tmpCanto.write(newLine)
closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto)
def addTercetTags(xmlCantoFile):
"""
add <tercet t_num="n"> ... </tercet> around each group of three lines until the end
"""
xmlCanto = getXMLFileName(xmlCantoFile)
tmpFile = '.' + xmlCantoFile + '.tmp'
with open(xmlCanto, 'r') as xmlCanto:
lastLine = xmlCanto.readlines()[-1]
lastLineNum = int(lastLine.split('"')[1])
xmlCanto.seek(0)
with open(tmpFile, 'w') as tmpCanto:
i = 0
j = 2
for line in xmlCanto:
#print(line)
i += 1
#print('new i: ' + str(i))
if i == 1:
newLine = '<tercet t_num="1">\n' + line
tmpCanto.write(newLine)
#print('first line newline: ' + newLine)
elif i % 3 == 0:
tercetNum = str(j)
newLineSub = '\n</tercet>\n<tercet t_num="' + tercetNum + '">\n'
newLine = re.sub('\n', newLineSub, line)
tmpCanto.write(newLine)
j += 1
#print('every 3 lines newline: ' + newLine)
elif i == lastLineNum:
newLine = re.sub('\n', '\n</tercet>', line)
tmpCanto.write(newLine)
#print('last line newline: ' + newLine)
else:
tmpCanto.write(line)
closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto)
def wrapTags(tag, xmlCantoFile):
"""
used to add <tercets>, <canto>, and <text> tags around <canto> body
"""
xmlCanto = getXMLFileName(xmlCantoFile)
if tag == 'TEI':
openTag = '<TEI xmlns="http://www.tei-c.org/ns/1.0">\n'
else:
openTag = '<' + tag + '>\n'
closeTag = '\n</' + tag + '>'
with open(xmlCanto, 'r') as xmlCantoContents:
contents = xmlCantoContents.read()
with open(xmlCanto, 'w') as xmlCantoContents:
xmlCantoContents.write(openTag)
xmlCantoContents.write(contents)
xmlCantoContents.write(closeTag)
xmlCantoContents.close()
def addTEIHeaderTags(xmlCantoFile):
"""
The following is the minimum required TEI header
<TEI xmlns="http://www.tei-c.org/ns/1.0">
<fileDesc>
<titleStmt>
<title>
</title>
<author>
</author>
<respStmt>
</respStmt>
</titleStmt>
<publicationStmt>
<publisher>
</publisher>
</publicationStmt>
<sourceDesc>
<citation>
</citation>
</sourceDesc>
</fileDesc>
<text>
"""
xmlCanto = getXMLFileName(xmlCantoFile)
def addSpaces(xmlCantoFile):
"""
Add a given number of spaces before each opening and closing tag.
User specifies the number of spaces only once, in a series of lists
which hold strings of each tag that needs spaces before it. This
function parses the names of the list to determine the number of
spaces to add before each tag (e.g. list2 => 2 spaces). It replaces
the tags in the document with the spaces affixed.
"""
# XML is hierarchical, so each "level" needs its own number of spaces
levelDict = {
'0' : ['TEI'],
'1' : ['teiHeader', 'text' ],
'2' : ['fileDesc' , 'canto' ],
'3' : ['titleStmt', 'tercets'],
'4' : [ 'tercet' ],
'5' : [ 'l' ]
}
#levelDictLen = int(sorted(levelDict.keys())[-1]) + 1 # number of levels. add 1 to make range() work below
tags = ['TEI', 'teiHeader', 'text', 'fileDesc', 'canto', 'titleStmt', 'tercets', 'tercet', 'l' ]
xmlCanto = getXMLFileName(xmlCantoFile)
tmpFile = '.' + xmlCantoFile + '.tmp'
with open(xmlCantoFile, 'r') as xmlCanto:
with open(tmpFile, 'w') as tmpCanto:
for line in xmlCanto:
for tag in tags:
# need open tag regex b/c we don't know if tag has attributes,
# and if we did, we might not know their value
openTagRegex = '(<' + tag + ')(>|\s.*>)' # match <tag> and <tag attr="value"> but not <tags>
closeTag = '</' + tag + '>'
for level in levelDict.keys():
if tag in levelDict[level]:
spaces = ' ' * int(level)
if re.search(openTagRegex, line):
newLine = re.sub(openTagRegex, spaces + r"\1\2", line)
tmpCanto.write(newLine)
elif closeTag in line:
if tag != 'l': # don't add spaces to closing line tags
newLine = re.sub(closeTag, spaces + closeTag, line)
tmpCanto.write(newLine)
closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto)
def closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto):
tmpCanto.close()
xmlCanto.close()
shutil.copy(tmpFile, xmlCantoFile)
os.remove(tmpFile)
def main():
for cantoFile in os.listdir(cantoDir):
xmlCantoName = getXMLFileName(cantoFile)
if re.search('\.txt$', cantoFile):
addWordTags(cantoFile)
addLineTags(xmlCantoName)
addTercetTags(xmlCantoName)
wrapTags('tercets', xmlCantoName)
wrapTags('canto', xmlCantoName)
wrapTags('text', xmlCantoName)
#addTEIHeaderTags()
wrapTags('TEI', xmlCantoName)
addSpaces(xmlCantoName)
os.remove(cantoFile) # don't delete txt files while debugging so you don't have to keep copying them
main()