import time 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 `` 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 `` 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 = '' closeWord = '' 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 to the beginning of each line number n add 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 ' \n' newLine = re.sub('\n', ' \n', line) newLine = ' ' + newLine tmpCanto.write(newLine) i += 1 #tmpCanto.write(' ') # add last closing tag because no \n to substitute closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto) def addTercetTags(xmlCantoFile): """ add ... 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] # still a \n at the end! lastLineNum = getLineNum(lastLine) xmlCanto.seek(0) with open(tmpFile, 'w') as tmpCanto: j = 2 for line in xmlCanto: if getLineNum(line) == 1: newLine = '\n' + line tmpCanto.write(newLine) elif getLineNum(line) == lastLineNum: newLine = re.sub('\n', '\n', line) tmpCanto.write(newLine) elif getLineNum(line) % 3 == 0: tercetNum = str(j) newLineSub = '\n\n\n' newLine = re.sub('\n', newLineSub, line) tmpCanto.write(newLine) j += 1 else: tmpCanto.write(line) closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto) def getLineNum(line): lineNum = int(line.split('"')[1]) return lineNum def wrapTags(tag, xmlCantoFile): """ used to add , , and tags around body """ xmlCanto = getXMLFileName(xmlCantoFile) cantoNum = str(int(xmlCanto.split('-')[0])) canticle = xmlCanto.split('-')[1] if canticle == 'inferno': comedyNum = cantoNum elif canticle == 'purgatorio': comedyNum = str(int(cantoNum) + 34) else: comedyNum = str(int(cantoNum) + 67) if tag == 'TEI': openTag = '\n' elif tag == 'canto': openTag = '<' + tag + ' canticle="' + canticle + '" c_num="' + cantoNum + '" comm_num="' + comedyNum +'">\n' else: openTag = '<' + tag + '>\n' closeTag = '\n' 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 addTEIHeader(xmlCantoFile): """ The following is the minimum required TEI header """ xmlCanto = getXMLFileName(xmlCantoFile) def addXMLHeader(xmlCantoFile): xmlCanto = getXMLFileName(xmlCantoFile) xmlHeader = '\n\n' with open(xmlCanto, 'r') as xmlCantoContents: contents = xmlCantoContents.read() with open(xmlCanto, 'w') as xmlCantoContents: xmlCantoContents.write(xmlHeader) xmlCantoContents.write(contents) xmlCantoContents.close() 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 and but not closeTag = '' 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) wrapTags('TEI', xmlCantoName) addSpaces(xmlCantoName) addXMLHeader(xmlCantoName) os.remove(cantoFile) # don't delete txt files while debugging so you don't have to keep copying them main()