{"id":517,"date":"2021-06-19T16:07:56","date_gmt":"2021-06-19T15:07:56","guid":{"rendered":"https:\/\/glennrowe.net\/programmingpages\/?p=517"},"modified":"2021-06-19T16:07:56","modified_gmt":"2021-06-19T15:07:56","slug":"writing-and-reading-text-files","status":"publish","type":"post","link":"https:\/\/glennrowe.net\/programmingpages\/2021\/06\/19\/writing-and-reading-text-files\/","title":{"rendered":"Writing and reading text files"},"content":{"rendered":"<p>Writing and reading text files in Python is fairly straightforward. It&#8217;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.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">def deck():\r\n    \"\"\" Generate a full deck of 52 cards as a list of tuples \"\"\"\r\n    suits = ['Spades', 'Hearts', 'Diamonds', 'Clubs']\r\n    rank = ['Ace'] + list(range(2,11)) + ['Jack', 'Queen', 'King']\r\n    cards = []\r\n    for suit in suits:\r\n        for spot in rank:\r\n            cards += [(suit, spot)]\r\n    return cards\r\n\r\ncardFile = open('Cards.txt', 'w')\r\ncardFile.writelines([str(x) + '\\n' for x in deck()])\r\ncardFile.close()\r\n\r\ncardFile = open('Cards.txt','r')\r\nfor x in cardFile:\r\n    print(x, end = '')\r\ncardFile.close()\r\n\r\ncardFile = open('Cards.txt','r')\r\ncardText = cardFile.readlines()\r\nprint(cardText)\r\nfor x in cardText:\r\n    print(x, end = '')\r\n<\/pre>\n<p>The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">deck()<\/code> function generates the cards, and is the same function as that used in the exercise in the <a href=\"https:\/\/glennrowe.net\/programmingpages\/2021\/05\/21\/functions-definition-and-simple-arguments\/\">post on functions<\/a>, 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.<\/p>\n<p>Lines 11 to 13 show how to open a file for writing. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">open()<\/code> function takes the file&#8217;s name and a string indicating whether you want to write to (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">'w'<\/code>) or read from (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">'r'<\/code>) the file. If the file isn&#8217;t in the same directory as the one in which the Python code is being run, you will need to prefix the file&#8217;s name with the full path to the file. Here, we are writing to Cards.txt in the same directory as the program. <strong>Important: note that opening a file for writing does not check whether that file exists, so you won&#8217;t get a chance to avoid overwriting the file if it exists.<\/strong> More on this below.<\/p>\n<p>On line 12, the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">writelines()<\/code> 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 <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cards<\/code> list to a string. If we tried replacing line 12 with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cardFile.writelines([x for x in deck()])<\/code>, we would get an error.<\/p>\n<p>On line 13 we call <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">close()<\/code> to finish the writing process. Usually, writing to a file will complete satisfactorily without a call to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">close()<\/code>, but it&#8217;s good practice to call it anyway to make sure that any data stored in memory is flushed and written to the file.<\/p>\n<p>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 <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">for x in cardFile<\/code> reads the next line in the file and stores it, as a string, in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x<\/code>. There&#8217;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 <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">x<\/code>.<\/p>\n<p>The advantage of doing things this way is that you&#8217;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&#8217;s best to treat each line separately.<\/p>\n<p>Reading a line includes the new line character <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">'\\n'<\/code> at the end of each line, so to avoid the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">print()<\/code> statement on line 17 from double-spacing the output, we specify that it should end each print with a null character, hence the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">end = ''<\/code>.<\/p>\n<p>Lines 20 to 24 show how you can read the whole file into memory if you want to. The <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">readlines()<\/code> method reads the entire file and stores it in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cardText<\/code>, which is a list. We can then iterate over <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">cardText<\/code> to print out the data, or do whatever we want with it.<\/p>\n<p>There are several other methods that can be used to vary how you read from or write to a file, but we won&#8217;t go into details here. I&#8217;ll just mention that you can read the single next line in a file with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">readline()<\/code>, and write a single line with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">writeline()<\/code>.<\/p>\n<h2>Open and save dialogs<\/h2>\n<p>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\u00a0<em>could<\/em> 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&#8217;s built-in dialogs for opening and saving files. Fortunately, this is fairly easy to do.<\/p>\n<p>The dialogs make use of the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tkinter<\/code> module, which is a package that provides graphical user interface (GUI) features for use with Python. There are many GUI packages available, but <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tkinter<\/code> has the advantage that it&#8217;s bundled with the standard Python release, so it should be available on any Python installation.<\/p>\n<p>We&#8217;ll deal with some of the other features in <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tkinter<\/code> later. For now, we&#8217;ll use just enough to allow us to request file names using dialog boxes. This won&#8217;t give us the cleanest of interfaces, but it&#8217;s enough to make the program functional. It&#8217;s easiest if we just show the program and then explain how it works.<\/p>\n<p>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.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">from tkinter import filedialog\r\n\r\ndef deck():\r\n    \"\"\" Generate a full deck of 52 cards as a list of tuples \"\"\"\r\n    suits = ['Spades', 'Hearts', 'Diamonds', 'Clubs']\r\n    rank = ['Ace'] + list(range(2,11)) + ['Jack', 'Queen', 'King']\r\n    cards = []\r\n    for suit in suits:\r\n        for spot in rank:\r\n            cards += [(suit, spot)]\r\n    return cards\r\n\r\nfiletypes = [['Text', '*.txt'], ['Python &amp; Text', '*.py *.txt']]\r\ninitialdir = '.'\r\n\r\nwriteFile = filedialog.asksaveasfile(title = 'Save file',\r\n                                     initialdir = initialdir,\r\n                                     filetypes = filetypes)\r\nif writeFile != None:\r\n    writeFile.writelines([str(x) + '\\n' for x in deck()])\r\n    writeFile.close()\r\n\r\nreadFile = filedialog.askopenfile(title = 'Open file', \r\n                          initialdir = initialdir,\r\n                          filetypes = filetypes)\r\n\r\nif readFile != None:\r\n    for line in readFile:\r\n        print(line, end = '')<\/pre>\n<p>We need to import <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">filedialog<\/code> on line 1 to use the dialogs. Lines 3 through 11 are the same <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">deck()<\/code> function we used above, with no changes.<\/p>\n<p>If you&#8217;re used to file dialogs in other programs, you&#8217;ll know that they have a few options that can come in handy. First, we&#8217;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 <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">filetypes<\/code> 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 <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">*.txt<\/code>. The second list chooses files that have either a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">*.py<\/code> or a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">*.txt<\/code> template, so they are either Python source files or text files. Note that the file templates are separated with a blank, so we have <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">'*.py *.txt'<\/code>.<\/p>\n<p>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 <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">'.'<\/code>), so its inclusion here is redundant, but it&#8217;s useful to know how to change it if you want to.<\/p>\n<p>On line 16, we call <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">asksaveasfile()<\/code> and pass in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">filetypes<\/code> filter and the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">initialdir<\/code>, along with a title for the dialog box. This line uses <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">tkinter<\/code> 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&#8217;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.<\/p>\n<p>The advantage of using the system&#8217;s dialog is that if you attempt to overwrite an existing file, you will get a confirmation box asking if this is OK.<\/p>\n<p>If the user chooses a file, this is returned in the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">writeFile<\/code> object on line 16. If the user clicks Cancel in the dialog, then the Python <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">None<\/code> object is returned, so you can abort the save if the user decides not to go ahead.<\/p>\n<p>Opening a file for reading uses the <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">askopenfile()<\/code> method on line 23, and works much the same way as the save dialog.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Writing and reading text files in Python is fairly straightforward. It&#8217;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 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-517","post","type-post","status-publish","format-standard","hentry","category-python","entry"],"_links":{"self":[{"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/posts\/517","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/comments?post=517"}],"version-history":[{"count":1,"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/posts\/517\/revisions"}],"predecessor-version":[{"id":518,"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/posts\/517\/revisions\/518"}],"wp:attachment":[{"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/media?parent=517"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/categories?post=517"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/glennrowe.net\/programmingpages\/wp-json\/wp\/v2\/tags?post=517"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}