วิธีคิดแบบนักวิทยาการคอมพิวเตอร์/Files and exceptions

บทที่ 11 Files and exceptions แก้ไข

ขณะที่โปรแกรมกำลังทำงานอยู่นั้น ข้อมูลต่างๆ จะถูกเก็บไว้ในหน่วยความจำ แต่เมื่อปิดการทำงานของโปรแกรมหรือปิดคอมพิวเตอร์แล้ว ข้อมูลที่อยู่ในหน่วยความจำจะหายไป การเก็บรักษาข้อมูลให้คงอยู่นั้น คุณจะต้องใส่มันไว้ในไฟล์ ( file) โดยปกติแล้ว files จะถูกเก็บไว้ใน Hard drive , floppy drive หรือ CD-ROM

เมื่อมีจำนวนไฟล์มาก บ่อยครั้งที่ไฟล์เหล่านั้นจะถูกรวบรวมไว้ใน directories หรือที่เรียกว่าโฟวเดอร์ ( Folders ) โดยแต่ละไฟล์จะมีที่ชื่อพิเศษ หรือชื่อไฟล์รวมกับชื่อของ directory

พอเวลาอ่านหรือเขียนไฟล์ โปรแกรมสามารถแลกเปลี่ยนข้อมูลในแต่ละที่ได้ และสร้าง รูปแบบที่เหมาะกับการพิมพ์ ( PDF )

การทำงานกับไฟล์จำนวนมากนั้น เหมือนการใช้งานหนังสือ เพราะในการใช้หนังสือนั้นคุณจะต้องเปิดมันก่อน และเมื่อใช้งานเสร็จแล้วคุณก็ปิดมัน ขณะที่เปิดหนังสืออยู่นั้น คุณสามารถอ่านหรือเขียนหนังสือได้ ในกรณีนี้ก็เช่นกัน คุณรู้ว่าคุณใช้งานอยู่ตรงส่วนไหนของหนังสือ ส่วนมากแล้วคุณจะอ่านหนังสือทั้งเล่มตามลำดับ แต่คุณก็สามารถข้ามไปส่วนอื่นได้เช่นกัน

การนำไฟล์มาใช้งานนั้น คือไปเปิดไฟล์ และกำหนดชื่อ จากนั้นบอกว่าต้องการที่จะอ่านหรือเขียนข้อมูล

เปิดไฟล์ และสร้าง file object ในตัวอย่างนี้ ตัวแปร f หมายถึง new file object

>>> f = open("test.dat","w")

>>> print f

<open file 'test.dat', mode 'w' at fe820>

เปิด function ที่มี 2 argument อันแรกคือชื่อของไฟล์ และอันดับที่สอง คือ รูปแบบ ( w ) รูปแบบของไฟล์นี้หมายถึง เรากำลังเปิดไฟล์สำหรับการเขียน

ถ้าไม่มีชื่อไฟล์ test.dat มันจะถูกสร้างขึ้น ถ้ามีไฟล์นี้อยู่แล้วมันจะถูกแทนที่ด้วยไฟล์ที่เราเขียนขึ้นใหม่

เมื่อเรา print file object เราจะเห็นชื่อของไฟล์ รูปแบบ และตำแหน่งของไฟล์ ใส่ข้อมูลลงในไฟล์ที่เราร้องขอ เพื่อเขียน method บนไฟล์ object

>>> f.write("Now is the time")

>>> f.write("to close the file")

ปิดไฟล์ในระบบที่เราเขียนเสร็จแล้ว และสร้างไฟล์ที่สามารถอ่านได้

>>> f.close()

ขณะนี้เราสามารถเปิดไฟล์ได้อีกครั้ง ครั้งนี้เพื่อการอ่านเนื้อหาและหัวข้อใน string เวลานี้ argument มีรูปแบบสำหรับการอ่าน “ r ”

>>> f = open("test.dat" ," r ")

หากเราพยายามที่จะเปิดไฟล์ที่ไม่มีจะไม่สามารถเปิดได้

>>> f = open("test.cat","r")

IOError: [Errno 2] No such file or directory: 'test.cat'

อย่าแปลกใจหากเราอ่านรูปแบบข้อมูลจากไฟล์ ไม่มีargument

>>> text = f.read()

>>> print text

Now is the timeto close the file

ไม่มีที่ว่างระหว่าง “time” และ “to” เพราะเราไม่เขียน space ระหว่าง string เวลาอ่านก็เช่นเดียวกัน สามารถกำหนดตัวอักษรที่ต้องการอ่านได้

>>> f = open("test.dat","r")

>>> print f.read(5)

Now i

ถ้ามีตัวอักษรไม่เพียงพอจะออกจากไฟล์ และกลับมาอ่านตัวอักษรที่เหลือ เมื่อเราไปถึงจุดสุดท้ายของไฟล์ จะกลับมาอ่านใน string ที่ว่าง

>>> print f.read(1000006)

s the timeto close the file

>>> print f.read()

>>>

ในฟังก์ชันการ copy ไฟล์ต่อมา การอ่านและเขียน เพิ่มได้ถึง 50 ตัวอักษร argument แรกเป็นชื่อของไฟล์เดิม อันที่สองเป็นชื่อของไฟล์ใหม่

def copyFile(oldFile, newFile):

     f1 = open(oldFile, "r")
     f2 = open(newFile, "w")
     while True:

text = f1.read(50)

if text == "":

     break

f2.write(text)

     f1.close()
     f2.close()
     return

การแยก statement ใหม่ การทำให้ statement แยกออกจาก loop การเลื่อน execution ไปเป็น statement แรก ภายหลังจาก loop ในตัวอย่างนี้ ที่ loop ไม่สิ้นสุดเพราะค่า True เป็นค่าที่ถูกเสมอ วิธีเดียวที่จะออกจาก loop นี้ได้คือ execute break สิ่งที่ปรากฏเมื่อ text เป็น string ที่ว่างซึ่งเกิดขึ้นเมื่อเราเข้าไปจนจบ

11.1 Text files แก้ไข

A text file เป็นไฟล์ที่บรรจุตัวอักษรที่สามารถพิมพ์ได้กับ whitespace และรวมไว้ในเส้นแบ่งบรรทัด ตั้งแต่ Python มีลักษณะการออกแบบเฉพาะเพื่อจัดการกับ text files มันจัดหา methods ที่ทำให้การทำงานง่ายขึ้น

ตัวอย่างการสร้าง text file ด้วย three line ของ text โดยใช้ newline

>>> f = open("test.dat","w")

>>> f.write("line one\nline two\nline three\n")

>>> f.close()

readline method จะอ่านตัวอักษรทั้งหมดรวมกับ next newline character

>>> f = open("test.dat","r")

>>> print f.readline()

line one

>>>

Readline return ค่าทุกบรรทัดที่เป็น list ของ string

>>> print f.readlines()

['line two\012', 'line three\012']

ใน case นี้ผลลัพธ์อยู่ในรูปแบบของ list ซึ่งหมายความว่า strings ปรากฏในเครื่องหมายคำพูด และ newline character ที่ปรากฏออกมาตามลำดับ 012

	สุดท้าย  readline จะ return ค่า string  ที่ว่างเปล่า และ  readline จะ return ค่า list ที่ว่างเปล่า

>>> print f.readline()

>>> print f.readlines()

[] ในตัวอย่างของโปรแกรม line-processing โดย filterFile ทำการคัดลอก oldFile และละเว้นบรรทัดที่ขึ้นต้นด้วย #

      def filterFile(oldFile, newFile):

f1 = open(oldFile, "r")

f2 = open(newFile, "w")

while True:

     text = f1.readline()
     if text == "":

break

     if text[0] == '#':

continue

f2.write(text)
                     f1.close()
       f2.close()
       return

การดำเนินการของ statement จะหยุดเมื่อเกิดการวนของ loop แต่ loop จะทำงานต่อไป Flow of execution เคลื่อนที่ไปบนสุดของ loop เพื่อตรวจสอบเงื่อนไข และดำเนินงานต่อไป

ดังนั้น ถ้า text เป็น string ที่ว่างอยู่ loop จะจบการทำงาน ถ้าตัวอักษรตัวแรกของ text มีปัญหา flow of execution ตรงส่วนบนสุดของ loop นอกจากเงื่อนไขทั้ง 2 ผิดพลาด เราจะคัดลอก text ไว้ใน new file

11.2 Writing variables แก้ไข

Argument ของ write เป็น string ดังนั้น ถ้าเราต้องการใส่ค่าอื่นลงใน file เราจะต้องเปลี่ยนมันเป็น string ก่อน ทางที่ง่ายคือการใช้ str function

>>> x = 52

>>> f.write (str(x))

ทางเลือกคือการใช้ format operator % เมื่อปรับไปเป็น integers ซึ่ง % เป็นเกณฑ์ควบคุม แต่เมื่อการคำนวณแรกเป็น string แล้ว  % จะเป็นรูปแบบของคำสั่ง

ในการทำงานครั้งแรก คือ format string และการทำงานต่อมาคือ คือ tuple ของสูตร

ผลลัพธ์ของมันคือ string ที่บรรจุค่าของสูตรไว้ ในรูปแบบที่กำหนดเพื่อ format string

ดังเช่นตัวอย่าง format sequence “%d” หมายถึง มันเป็นสูตรแรกใน tuple ที่ควรจะ formatted เช่นเดียวกับ integer ในตัวอักษร d ที่กำกับไว้นี้หมายถึง “decimal” หรือเลขทศนิยม

>>> cars = 52

>>> "%d" % cars

'52'

ผลลัพธ์คือ string '52' ที่ไม่สับสนกันกับค่า integer 52

The format sequence สามารถแสดงขึ้นได้ทุกที่ในการ format string ดังนั้นเราสามารถใส่ค่าใน sentence ได้

>>> cars = 52

>>> "In July we sold %d cars." % cars

'In July we sold 52 cars.'

The format sequence “%f” รูปแบบ ใน item อันถัดไปใน tuple ที่จำนวนมีจุดทศนิยม และ “%s” เป็นรูปแบบใน item ที่เป็น string

>>> "In %d days we made %f million %s." % (34,6.1,'dollars')

'In 34 days we made 6.100000 million dollars.'

โดยปกติ เลขทศนิยมจะทำการ prints ค่าออกมา 6 หลัก

จำนวนของ expressions ใน tuple นั้นมีความคล้ายกับจำนวนของ format sequences ใน string เช่นเดียวกัน ชนิดของ expressions ก็เข้ากันกับ format sequences

>>> "%d %d %d" % (1,2)

TypeError: not enough arguments for format string

>>> "%d" % 'dollars'

TypeError: illegal argument type for built-in operation

ในตัวอย่างแรกมันมี expressions ไม่เพียงพอ ในตัวอย่างที่ 2 ชนิดของ expression ไม่ถูกต้อง สำหรับการควบคุมที่มากกว่ารูปแบบของตัวเลข เราสามารถกำหนดจำนวนของตัวเลขในแต่ละส่วนของ format sequence ได้

>>> "%6d" % 62

' 62'

>>> "%12f" % 6.1

' 6.100000'

ตัวเลขที่อยู่ข้างหลัง เครื่องหมายเปอร์เซ็นต์นั้นคือจำนวนที่น้อยที่สุดของที่ว่างในจำนวนที่จะเริ่มต้น ถ้าค่าที่ได้มีจำนวนหลักน้อยกว่า ที่ว่างจะได้รับการเพิ่ม ถ้าจำนวนของช่องว่างเป็นค่าลบ ช่องว่างด้านหลังจะเพิ่มขึ้น

>>> "%-6d" % 62

'62 '

สำหรับตัวเลขทศนิยม เราสามารถเจาะจงหลักของจำนวนหลังจาก decimal point

>>> "%12.2f" % 6.1

' 6.10'

ในตัวอย่างนี้ ผลลัพธ์ทำให้เกิดช่องว่างถึง 12 และเพิ่มอีก 2 หลักหลังจุดทศนิยม นี่เป็นรูปแบบที่เป็นประโยชน์สำหรับการพิมพ์ผลรวมของ dollar ให้อยู่ในหลักเดียวกัน

ตัวอย่างแนวคิดสำหรับปทานุกรมนั้น เช่นการบรรจุชื่อเป็น keys และค่าจ้างเป็นผลลัพธ์ นี่คือ function ที่จะ prints เนื้อหาของปทานุกรมเหมือนการ formatted report

def report (wages) :

      students = wages.keys()
      students.sort()
      for student in students :

print "%-20s %12.2f" % (student, wages[student])

ทดสอบฟังก์ชันนี้ เราจะทำปทานุกรมเล็กๆ และพิมพ์เนื้อหา

>>> wages = {'mary': 6.23, 'joe': 5.45, 'joshua': 4.25}

>>> report (wages)

joe 5.45

joshua 4.25

mary 6.23

ในการควบคุมความกว้างของแต่ละค่า รับประกันว่าคอลัมน์จะเป็นแถว ถ้าชื่อน้อยกว่า 21 ตัวอักษร และค่าจ้างน้อยกว่า 1 พันล้าน dollars ต่อชั่วโมง

11.3 Directories แก้ไข

เมื่อคุณสร้างแฟ้มใหม่โดยที่เปิดมัน และเขียน แฟ้มใหม่จะไปอยู่ใน directory ปัจจุบัน

( ที่ใดก็ตามที่ เมื่อคุณใช้งานโปรแกรมอยู่ ) เช่นเดียวกัน เมื่อคุณเปิดแฟ้มขึ้นมาอ่าน Python จะค้นหามันใน directory ปัจจุบัน

ถ้าคุณต้องการเปิดไฟล์ที่อื่น คุณต้องเจาะจงเส้นทางเดินของไฟล์ ซึ่งเป็นชื่อของ directory ( หรือ Folder ) ที่ๆ มันอยู่

>>> f = open("/usr/share/dict/words","r")

>>> print f.readline()

Aarhus

ในตัวอย่างนี้ เป็นการเปิดไฟล์ที่ชื่อ “words” ที่อยู่ใน directory ชื่อ dict และอยู่ภายใน share ที่อยู่ใน usr ที่เป็น directory บนสุดของระบบ หรือเรียกว่า /

คุณไม่สามารถใช้ / เหมือนเป็นส่วนหนึ่งของ filename ได้ เพราะมันถูกสงวนไว้สำหรับกั้นระหว่าง directory กับ filename

ไฟล์ /usr/share/dict/words จะบรรจุรายการเรียงตามลำดับตัวอักษร ชื่อแรกของมันเป็นชื่อของ Danish University

11.4 Pickling แก้ไข

ในคำสั่งให้ใส่ค่าลงไปในไฟล์ คุณต้องเปลี่ยนมันเป็น string คุณต้องมองเห็นแล้วว่าจะทำ อย่างไรกับตัว str นี้

>>> f.write (str(12.3))

>>> f.write (str([1,2,3]))

ปัญหาคือ เมื่อคุณต้องการอ่านค่าย้อนหลัง คุณ get a string ชนิดของข้อมูลเดิมต่างๆจะสูญหาย ในความจริงแล้วคุณไม่สามารถบอกได้เสมอว่าผลลัพธ์เหล่านี้เริ่มและจบที่ไหน

>>> f.readline()

'12.3[1, 2, 3]'

การแก้ปัญหา คือ pickling ที่เรียกเช่นนั้นเพราะมันเก็บรักษาโครงสร้างข้อมูลไว้ The pickle module บรรจุคำสั่งที่จำเป็น การใช้งานจะต้องนำเข้า pickle และเปิดไฟล์ตามปกติ

>>> import pickle

>>> f = open("test.pck","w")

การเก็บโครงสร้างข้อมูลนั้นใช้ dump method และจากนั้นปิดไฟล์ตามปกติ

>>> pickle.dump(12.3, f)

>>> pickle.dump([1,2,3], f)

>>> f.close()

จากนั้นเราสามารถเปิดไฟล์เพื่ออ่านและโหลดโครงสร้างของข้อมูล

>>> f = open("test.pck","r")

>>> x = pickle.load(f)

>>> x

12.3

>>> type(x)

<type 'float'>

>>> y = pickle.load(f)

>>> y

[1, 2, 3]

>>> type(y)

<type 'list'>

แต่ละครั้งที่เรียกการโหลด เราได้รับ single value จากไฟล์ ที่สมบูร์และเป็น original type

11.5 Exceptions แก้ไข

เมื่อใดก็ตามที่เกิด runtime error ขึ้น มันจะสร้าง exception โดยปกติแล้วโปรแกรมจะหยุดทำงาน และ Python จะ print ข้อความแสดงข้อผิดพลาดให้ เช่น การหารโดยใช้ 0

>>> print 55/0

ZeroDivisionError: integer division or modulo หรือการเข้าไปใช้งาน ในส่วนที่ไม่มีอยู่จริง

>>> a = []

>>> print a[5]

IndexError: list index out of range หรือการเข้าไปที่ key ที่มันไม่มีอยู่ใน directory

>>> b = {}

>>> print b['what']

KeyError: what หรือการพยายามเปิดไฟล์ที่มันไม่มี

>>> f = open("Idontexist", "r")

IOError: [Errno 2] No such file or directory: 'Idontexist'

ในแต่ละ case นั้น ข้อความที่แสดงข้อผิดพลาดจะมีอยู่ 2 ส่วน คือ ชนิดของความผิดพลาดจะอยู่ข้างหน้าเครื่องหมาย  : และส่วนขยายความหลังเครื่องหมาย  : โดยปกติ Python ก็เหมือนกันที่จะ print ตัว tracback ของโปรแกรมที่มันอยู่ แต่เราได้ยกเว้นไว้จากตัวอย่าง

บางครั้งเราต้องดำเนินการ execute ที่เป็นเหตุของ exception ( ข้อยกเว้น ) แต่เราไม่ต้องการให้โปรแกรมหยุด เราสามารถ handle กับข้อยกเว้น โดยการใช้ try และ except statement

ในตัวอย่างนี้เราจะทบทวน สำหรับชื่อไฟล์และเมื่อพยายามที่จะเปิดมัน ถ้าไฟล์ที่เรียกไม่มี เราไม่ต้องการให้โปรแกรมดับไป เราต้องการ handle the exception

filename = raw_input('Enter a file name: ')

try:

    f = open (filename, "r")

except IOError:

    print 'There is no file named', filename

การที่จะ execute ตัว statement ในบล็อกแรก ถ้าไม่มี exceptions เกิดขึ้น มันจะข้ามการ except statement ไป ถ้ามี exception ชนิด IOError เกิดขึ้น มันจะ executes ตัว statement ที่อยู่ใน except branch และจะทำงานต่อ

เราสามารถสรุปความสามารถของฟังก์ชันนี้ได้ คือ exists จัดการกับ filename และคืนค่าที่เป็นจริงกลับมา ถ้า file exists ผิดพลาด มันจะไม่ทำงาน

def exists(filename):

      try:

f = open(filename)

f.close()

return True

       except IOError:

return False

คุณสามารถใช้ except block ได้หลายอัน เพื่อ handle กับความต่างของชนิดใน exception The Python Reference Manual จะมีรายละเอียด

ถ้าโปรแกรมของคุณพบความผิดพลาด คุณสามารถ raise ตัว exception ได้

def inputNumber () :

      x = input ('Pick a number: ')
      if x == 17 :

raise ValueError, '17 is a bad number'

return x

การ raise statement ต้องเลือก 2 arguments คือชนิดของ exception และข้อมูลรายละเอียดเกี่ยวกับความผิดพลาดที่เกิดขึ้น ValueError เป็นหนึ่งในชนิดของ exception ที่ภาษา Python เตรียมไว้ให้เป็นทางเลือก มีตัวอย่างอื่นอีกเช่น TypeError, KeyError,และ my favorite, NotImplementedError.

ถ้าฟังก์ชันนี้เรียกว่า inputNumber การ handles กับข้อผิดพลาด เมื่อโปรแกรมสามารถทำงาน ต่อได้ อีกอย่างหนึ่งคือ Python จะ print ข้อความบ่งบอกข้อผิดพลาดและออกจากการทำงาน

>>> inputNumber ()

Pick a number: 17

ValueError: 17 is a bad number

ข้อความบ่งบอกข้อผิดพลาดนี้จะรวมชนิดของ exception และข้อมูลเพิ่มเติมไว้ด้วย

As an exercise, write a function that uses inputNumber to input a number from the keyboard and that handles the ValueError exception.

11.6 Glossary แก้ไข

file: A named entity, usually stored on a hard drive, floppy disk, or CD-ROM, that contains a stream of characters.

directory: A named collection of files, also called a folder.

path: A sequence of directory names that specifies the exact location of a file.

text file: A file that contains printable characters organized into lines separated by newline characters.

break statement: A statement that causes the flow of execution to exit a loop.

continue statement: A statement that causes the current iteration of a loop to end. The flow of execution goes to the top of the loop, evaluates the condition, and proceeds accordingly.

format operator: The % operator takes a format string and a tuple of expressions and yields a string that includes the expressions, formatted according to the format string.

format string: A string that contains printable characters and format sequences that indicate how to format values.

format sequence: A sequence of characters beginning with % that indicates how to format a value.

pickle: To write a data value in a file along with its type information so that it can be reconstituted later.

exception: An error that occurs at runtime.

handle: To prevent an exception from terminating a program using the try and except statements.

raise: To signal an exception using the raise statement.