Using files and handling errors

In addition to easy string handling, Python allows you to read, edit, and create files easily. So, by building upon the previous scripts, we can make use of our encryptText() function to encode complete files.

Reading and writing to files can be quite dependent on factors that are outside of the direct control of the script, such as whether the file that we are trying to open exists or the filesystem has space to store a new file. Therefore, we will also take a look at how to handle exceptions and protect operations that may result in errors.

Getting ready

The following script will allow you to specify a file through the command line, which will be read and encoded to produce an output file. Create a small text file named infile.txt and save it so that we can test the script. It should include a short message similar to the following:

This is a short message to test our file encryption program.

How to do it…

Create the fileencrypt.py script using the following code:

#!/usr/bin/python3
#fileencrypt.py
import sys #Imported to obtain command line arguments
import encryptdecrypt as ENC

#Define expected inputs
ARG_INFILE=1
ARG_OUTFILE=2
ARG_KEY=3
ARG_LENGTH=4

def covertFile(infile,outfile,key):
  #Convert the key text to an integer
  try:
    enc_key=int(key)
  except ValueError:
    print ("Error: The key %s should be an integer value!" % (key))
  #Code put on to two lines
  else:
    try:
      #Open the files
      with open(infile) as f_in:
        infile_content=f_in.readlines()
    except IOError:
      print ("Unable to open %s" % (infile))
    try:
      with open(outfile,'w') as f_out:
        for line in infile_content:
          out_line = ENC.encryptText(line,enc_key)
          f_out.writelines(out_line)
    except IOError:
      print ("Unable to open %s" % (outfile))
    print ("Conversion complete: %s" % (outfile))
  finally:
    print ("Finish")

#Check the arguments
if len(sys.argv) == ARG_LENGTH:
  print ("Command: %s" %(sys.argv))
  covertFile(sys.argv[ARG_INFILE], sys.argv[ARG_OUTFILE], sys.argv[ARG_KEY])
else:
  print ("Usage: fileencrypt.py infile outfile key")
#End

To run the script, use the following command (here, infile can be any text file we want to encrypt, outfile is our encrypted version, and key is the key value we wish to use):

python3 fileencrypt.py infile outfile key

For example, to encrypt infile.txt and output it as encrypted.txt using 30 as the key, use the following command:

python3 fileencrypt.py infile.txt encrypted.txt 30

To view the result, use less encrypted.txt. Press Q to exit.

To decrypt encrypted.txt and output it as decrypted.txt using -30 as the key, use the following command:

python3 fileencrypt.py encrypted.txt decrypted.txt -30

To view the result, use less decrypted.txt. Press Q to exit.

How it works…

The script requires us to use arguments that are provided on the command line. We will access them by importing the Python module called sys. Just like we did earlier, we will also import our encryptdecrypt module using the import command. We will use the as part to allow us to reference it using ENC.

Next, we will set values to define what each command-line argument will represent. When you run it, you will see that sys.argv[] is a list of values shown in the following array:

['fileencrypt.py', 'infile.txt', 'encrypted.txt', '30']

So, the input file is at the index 1 in the list (indexing always starts at 0), then the output file, and finally, the key, with the total number of arguments being ARG_LENGTH=4.

Next, we will define the convertFile() function, which we will call in a minute from the next block of code.

To avoid errors, we will check whether the length of the sys.argv value matches the expected number of arguments from the command line. This will ensure that the user has supplied us with enough, and we don't try to reference items in the sys.argv[] list that don't exist. Otherwise, we will return a short message explaining what we are expecting.

We will now call the convertFile() function using the command-line values and making use of Python's built-in exception handling features to ensure that errors are responded to accordingly.

The try…except code allows you to try running some code and handle any exceptions (errors) within the program itself, rather than everything coming to a sudden stop.

The try code is accompanied by the following four optional sections:

  • except ValueError: – When an error occurs, a specific type of exception can be specified and handled with the action, depending on the error we wish to handle (that is, for ValueError, we could check whether the value is a float value and convert it to an integer or prompt for a new one). Multiple exceptions can be caught using except (ValueError,IOError) as required.
  • except: – This is a catch-all case where any other exceptions that we haven't handled can be dealt with. For situations where the code may be called from other places, we may also want to raise the exception again using the raise command so that it can be dealt with by other parts of the program (for instance, as part of the GUI, we can warn the user that the input was not correct without needing to do so at this stage). Typically, you should either deal with a specific exception or ensure that you raise it again so that the particular error is visible on a failure; if not handled at all, Python will report it on the terminal along with the trace to the function where it occurred.
  • else: – This section of code is always executed if the try code was successful and there was no exception raised; however, any errors in this code will not be handled by the try…except section it is part of.
  • finally: – This code is always executed, regardless of whether an exception was raised or the try code ran without problems.

If you are familiar with other languages, you will find try…except similar to try…catch, and raise and throw as equivalents. Dealing with exceptions can be quite an art form; however, making your code able to handle problems gracefully and effectively is all part of good design. In many cases, catching the situations where things go wrong is just as important as performing the intended function successfully.

If there is no problem with converting the key argument into an integer, we will continue to open the input file specified and read the contents into the infile_content list. This will contain the contents of the file split into separate lines as a list.

Note

In this example, we will use a slightly different method to display values within the print statement.

Consider the following code as an example:

print ("Error: The key %s should be an integer value!" %(key))

This allows us to use the %s symbol to determine where the key value is printed and also to specify the format (%s is a string). For numerical values, such as floats and integers, we can use %d to display integers, %f for floats, or even %.4f to limit the value to four decimal places.

You may have noticed that we opened the file using the with…as…: section. This is a special way to open a file, which will ensure that it is closed once it has finished (even if there is an error). Refer to the following code:

try:
  #Open the files
  with open(infile) as f_in:
    infile_content=f_in.readlines()
except IOError:
  print ("Unable to open %s" % (infile))

This is equivalent to the following:

try:
  f_in = open(infile)
  try:
    infile_content=f_in.readlines()
  finally:
    f_in.close()
  except IOError:
    print ("Unable to open %s" % (infile))

If there is an exception in opening the file (if it doesn't exist, for example, it will raise IOError), we can flag to the user that there was a problem with the filename/path provided. We will also use except: on its own to deal with any other problems that we may have with the file, such as the encoding type or non-text based files.

Next, we will open a file for our output using 'w' to open it as a writable file. If it doesn't exist, it will create a new file; otherwise, it will overwrite the file. We will also have the option to append to the file instead, using 'a'. We will step through each item in infile_content, converting each line by passing it through our ENC.encryptText() function and writing the line to the f_out file. Once again, when we finish the with…as…: section, the file is closed and the conversion is complete.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.191.22