Writing and reading text files in Python is fairly straightforward. It’s easiest to illustrate the process with an example. Here is some code that generates the 52 cards in a standard deck of playing cards as a list of tuples. The cards are then saved to a text file, with one card per line, and then read back from the text file and printed out in the console.
def deck(): """ Generate a full deck of 52 cards as a list of tuples """ suits = ['Spades', 'Hearts', 'Diamonds', 'Clubs'] rank = ['Ace'] + list(range(2,11)) + ['Jack', 'Queen', 'King'] cards = [] for suit in suits: for spot in rank: cards += [(suit, spot)] return cards cardFile = open('Cards.txt', 'w') cardFile.writelines([str(x) + '\n' for x in deck()]) cardFile.close() cardFile = open('Cards.txt','r') for x in cardFile: print(x, end = '') cardFile.close() cardFile = open('Cards.txt','r') cardText = cardFile.readlines() print(cardText) for x in cardText: print(x, end = '')
The deck()
function generates the cards, and is the same function as that used in the exercise in the post on functions, so you can refer back to that if you want the details. The important point here is that the cards object that is returned is a list of tuples, with each tuple representing one card in the deck.
Lines 11 to 13 show how to open a file for writing. The open()
function takes the file’s name and a string indicating whether you want to write to ('w'
) or read from ('r'
) the file. If the file isn’t in the same directory as the one in which the Python code is being run, you will need to prefix the file’s name with the full path to the file. Here, we are writing to Cards.txt in the same directory as the program. Important: note that opening a file for writing does not check whether that file exists, so you won’t get a chance to avoid overwriting the file if it exists. More on this below.
On line 12, the writelines()
function takes as its argument any iterable object, such as a list or tuple. In this case, we use list comprehension to create a list of strings, with each string being the data for one card in the deck. Writing to a text file requires that the data to be written is a string, or an iterable object containing strings, so we need to convert each element in the cards
list to a string. If we tried replacing line 12 with cardFile.writelines([x for x in deck()])
, we would get an error.
On line 13 we call close()
to finish the writing process. Usually, writing to a file will complete satisfactorily without a call to close()
, but it’s good practice to call it anyway to make sure that any data stored in memory is flushed and written to the file.
Line 15 opens the file we just wrote for reading. A file object in Python is iterable, which means that the lines in the file can be treated as components of an iterable object. Thus we can use a for loop, as in line 16, to iterate over the lines. The statement for x in cardFile
reads the next line in the file and stores it, as a string, in x
. There’s actually a fair bit going on behind the scenes in this line, but for now, just accept that this for loop reads each line from the file in order and stores the current line in the variable x
.
The advantage of doing things this way is that you’re not reading the entire file into memory in one go. For very large disk files, attempting to read the whole thing into memory could cause a memory overflow, so unless you actually need the entire contents of the file in memory at the same time, it’s best to treat each line separately.
Reading a line includes the new line character '\n'
at the end of each line, so to avoid the print()
statement on line 17 from double-spacing the output, we specify that it should end each print with a null character, hence the end = ''
.
Lines 20 to 24 show how you can read the whole file into memory if you want to. The readlines()
method reads the entire file and stores it in cardText
, which is a list. We can then iterate over cardText
to print out the data, or do whatever we want with it.
There are several other methods that can be used to vary how you read from or write to a file, but we won’t go into details here. I’ll just mention that you can read the single next line in a file with readline()
, and write a single line with writeline()
.
Open and save dialogs
Once you see how to write and read files by hard-coding their names in the code, you will probably want to be able to ask the user to choose from a selection of file names. You could do this by asking the user to type the file name at an input prompt, but it would be nicer if you could use your operating system’s built-in dialogs for opening and saving files. Fortunately, this is fairly easy to do.
The dialogs make use of the tkinter
module, which is a package that provides graphical user interface (GUI) features for use with Python. There are many GUI packages available, but tkinter
has the advantage that it’s bundled with the standard Python release, so it should be available on any Python installation.
We’ll deal with some of the other features in tkinter
later. For now, we’ll use just enough to allow us to request file names using dialog boxes. This won’t give us the cleanest of interfaces, but it’s enough to make the program functional. It’s easiest if we just show the program and then explain how it works.
The following code does the same thing as the example above, except that dialog boxes are displayed to allow the user to specify which files should be used for writing and reading the data.
from tkinter import filedialog def deck(): """ Generate a full deck of 52 cards as a list of tuples """ suits = ['Spades', 'Hearts', 'Diamonds', 'Clubs'] rank = ['Ace'] + list(range(2,11)) + ['Jack', 'Queen', 'King'] cards = [] for suit in suits: for spot in rank: cards += [(suit, spot)] return cards filetypes = [['Text', '*.txt'], ['Python & Text', '*.py *.txt']] initialdir = '.' writeFile = filedialog.asksaveasfile(title = 'Save file', initialdir = initialdir, filetypes = filetypes) if writeFile != None: writeFile.writelines([str(x) + '\n' for x in deck()]) writeFile.close() readFile = filedialog.askopenfile(title = 'Open file', initialdir = initialdir, filetypes = filetypes) if readFile != None: for line in readFile: print(line, end = '')
We need to import filedialog
on line 1 to use the dialogs. Lines 3 through 11 are the same deck()
function we used above, with no changes.
If you’re used to file dialogs in other programs, you’ll know that they have a few options that can come in handy. First, we’d like to specify a filter so that only files with certain suffixes are shown in the dialog. We can do this by specifying a filetypes
list, as in line 13. The filters are specified as a list of lists (a list of tuples works as well). Each list within the overall list should contain two elements: the first is the name of the file type (which can be anything you like, but obviously you should choose something that explains what the file type is for). The second element is the actual filter. On line 13, we first specify text files, with the file template *.txt
. The second list chooses files that have either a *.py
or a *.txt
template, so they are either Python source files or text files. Note that the file templates are separated with a blank, so we have '*.py *.txt'
.
Line 14 specifies the directory in which the dialog opens. By default, it opens in the same directory as the one that contains the Python source code (which is specified by '.'
), so its inclusion here is redundant, but it’s useful to know how to change it if you want to.
On line 16, we call asksaveasfile()
and pass in the filetypes
filter and the initialdir
, along with a title for the dialog box. This line uses tkinter
to display the same dialog box you would see in any other program on your operating system. As I have access only to a Windows system, I can’t verify that it will work the same way on Linux or Macs, but if you find any variation, you can leave a comment below.
The advantage of using the system’s dialog is that if you attempt to overwrite an existing file, you will get a confirmation box asking if this is OK.
If the user chooses a file, this is returned in the writeFile
object on line 16. If the user clicks Cancel in the dialog, then the Python None
object is returned, so you can abort the save if the user decides not to go ahead.
Opening a file for reading uses the askopenfile()
method on line 23, and works much the same way as the save dialog.