Presentation is loading. Please wait.

Presentation is loading. Please wait.

File Input and Output (I/O) Engineering 1D04, Teaching Session 7.

Similar presentations


Presentation on theme: "File Input and Output (I/O) Engineering 1D04, Teaching Session 7."— Presentation transcript:

1 File Input and Output (I/O) Engineering 1D04, Teaching Session 7

2 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng1 using System.IO; File I/O  For anything in this session to work correctly, you need to include the File I/O Library.  This can be done by adding the following line at the top of your C# program with the rest of the using statements.

3 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng2 File I/O  Problem: Find the distribution of grade points from 1D04 last year, i.e. How many people received each grade point?

4 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng3 Grade Point File I/O  With this data, we should be able to produce a chart like this:

5 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng4 File I/O  There were 800 people in 1D04 last year. This is a lot of data to enter manually - if we have the grades already stored in a computer readable form.  When large amounts of data need to be used in a computer program it is essential to be able to import this data from a file.

6 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng5 File I/O  In this example, a sample file has been prepared called grades.yyz.  Each line has a number from 0-12 corresponding to the McMaster Grade Scale.  Student names are not included.

7 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng6 8 3 8 6 8 3 12 9 1 0 grades.yyz File I/O  Extract from the grade file

8 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng7 File I/O  Develop an algorithm to solve this problem?

9 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng8 0123456789101112 array indices File I/O - Solution  We can use an array, with each index in the array representing a grade point.

10 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng9  Each location will represent how many people scored that grade.  We need to zero each location as we have not read in any grades yet. 0123456789101112 0000000000000 array indices Why? File I/O - Solution

11 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng10 sequence 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000010000 index Increment Value by 1 File I/O - Solution  As we read through the file of grades, we will increment the value stored at the location of the corresponding grade point. That is why we zero the elements first

12 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng11 sequence 8 3 8 6 8 3 12 9 1 0 0123456789101112 0001000010000 index Increment Value by 1 File I/O - Solution  As we read through the file of grades, we will increment the value stored at the location of the corresponding grade point.

13 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng12 sequence 8 3 8 6 8 3 12 9 1 0 0123456789101112 0001000020000 index Increment Value by 1 File I/O - Solution  As we read through the file of grades, we will increment the value stored at the location of the corresponding grade point.

14 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng13 sequence 8 3 8 6 8 3 12 9 1 0 0123456789101112 0001001020000 index Increment Value by 1 File I/O - Solution  As we read through the file of grades, we will increment the value stored at the location of the corresponding grade point.

15 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng14 sequence 8 3 8 6 8 3 12 9 1 0 0123456789101112 0001001030000 index Increment Value by 1 File I/O - Solution  As we read through the file of grades, we will increment the value stored at the location of the corresponding grade point.

16 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng15 File I/O - Algorithm  Create and zero an array of 13 elements for grade points.  Input one grade from the grade file.  Increment the respective location in the grade point array.  Repeat until all grades have been used.

17 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng16 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O - Declarations  Streams are used to work with files.  A stream is simply a name for a flow of data.

18 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng17 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O - Declarations  Streams are used to work with files.  A stream is simply a name for a flow of data. This opens the stream for reading name of stream

19 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng18 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O - Declarations  The filename associated with the stream is a string passed as a parameter inside ()’s when creating a new stream.  To make it easier right now, the file needs to be in the working directory for your program.

20 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng19 File I/O - Declarations  In Visual Studio, the working directory for an application is:  projectFolder\ProjectName\bin\Debug  Example:  C:\Documents and Settings\username\ My Documents\Visual Studio 2005\Projects\ gradeThingy\gradeThingy\bin\Debug

21 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng20 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O  Create and zero an array of 13 elements for grade points.  Input one grade from the grade file.  Increment the respective location in the grade point array.  Repeat until there are no more grades in the file.

22 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng21 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O  Create and zero an array of 13 elements for grade points.  Input one grade from the grade file.  Increment the respective location in the grade point array.  Repeat until there are no more grades in the file. why?

23 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng22 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O  Create and zero an array of 13 elements for grade points.  Input one grade from the grade file.  Increment the respective location in the grade point array.  Repeat until there are no more grades in the file. why? assumption (at least 1)

24 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng23 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O  Create and zero an array of 13 elements for grade points.  Input one grade from the grade file.  Increment the respective location in the grade point array.  Repeat until there are no more grades in the file.

25 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng24 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O  Create and zero an array of 13 elements for grade points.  Input one grade from the grade file.  Increment the respective location in the grade point array.  Repeat until there are no more grades in the file.

26 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng25 Create and zero an array of 13 elements for grade points. Input one grade from the grade file. Increment the respective location in the grade point array. 2 3 1 4 Why doesn't our C# loop look like this? Is there another grade in the file? Loop Visualization

27 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng26 Create and zero an array of 13 elements for grade points. Input one grade from the grade file. Increment the respective location in the grade point array. 2 3 1 4 In C#, we can't ask the question Is there another grade in the file? Instead, we can ask the question Does the last grade we attempted to read, exist? Is there another grade in the file? Loop Visualization

28 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng27 Loop Visualization Create and zero an array of 13 elements for grade points. Input one grade from the grade file. Increment the respective location in the grade point array. Does the last grade we attempted to read, exist? 2 3 1 4 We have to adjust the loop slightly to accommodate this new question. Input one grade from the grade file. 3

29 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng28 void findGradeDistribution(){ int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt; inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O  inStream.ReadLine() is where all the magic happens.  inStream.ReadLine() returns a string value of the next unread line unless there are no more unread lines in which case it returns null.

30 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng29... int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt;... 0123456789101112 0000000000000 File I/O - Animation

31 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng30... int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt;... 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000000000 File I/O - Animation grades.yyz

32 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng31... int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt;... inputString 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000000000 File I/O - Animation grades.yyz

33 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng32... int[] gradeDistribution = new int[13]; StreamReader inStream = new StreamReader("grades.yyz"); string inputString; int inputInt;... inputStringinputInt 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000000000 File I/O - Animation grades.yyz

34 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng33 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "8" inputInt 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000000000 File I/O - Animation grades.yyz

35 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng34 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "8" inputInt 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000000000 True File I/O - Animation grades.yyz

36 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng35 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "8" inputInt 8 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000000000 File I/O - Animation grades.yyz

37 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng36 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "8" inputInt 8 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000010000 File I/O - Animation grades.yyz

38 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng37 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "3" inputInt 8 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000010000 File I/O - Animation grades.yyz

39 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng38 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "3" inputInt 8 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000010000 True File I/O - Animation grades.yyz

40 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng39 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "3" inputInt 3 8 3 8 6 8 3 12 9 1 0 0123456789101112 0000000010000 File I/O - Animation grades.yyz

41 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng40 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "3" inputInt 3 8 3 8 6 8 3 12 9 1 0 0123456789101112 0001000010000 File I/O - Animation grades.yyz

42 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng41 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "1" inputInt 1 8 3 8 6 8 3 12 9 1 0 0123456789101112 0002001031001 Magically Skipping Steps File I/O - Animation grades.yyz

43 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng42 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "1" inputInt 1 8 3 8 6 8 3 12 9 1 0 0123456789101112 0102001031001 File I/O - Animation grades.yyz

44 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng43 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "0" inputInt 1 8 3 8 6 8 3 12 9 1 0 0123456789101112 1102001031001 File I/O - Animation grades.yyz

45 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng44 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "0" inputInt 1 8 3 8 6 8 3 12 9 1 0 0123456789101112 0102001031001 File I/O - Animation grades.yyz True

46 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng45 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "0" inputInt 0 8 3 8 6 8 3 12 9 1 0 0123456789101112 0102001031001 File I/O - Animation grades.yyz

47 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng46 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "0" inputInt 0 8 3 8 6 8 3 12 9 1 0 0123456789101112 1102001031001 File I/O - Animation grades.yyz

48 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng47 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString "0" inputInt 0 8 3 8 6 8 3 12 9 1 0 0123456789101112 1102001031001 File I/O - Animation grades.yyz

49 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng48 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString null inputInt 0 8 3 8 6 8 3 12 9 1 0 0123456789101112 1102001031001 File I/O - Animation grades.yyz

50 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng49 inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); }... inputString null inputInt 0 8 3 8 6 8 3 12 9 1 0 False 0123456789101112 1102001031001 File I/O - Animation grades.yyz

51 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng50... inputString = inStream.ReadLine(); while (inputString != null){ inputInt = Convert.ToInt32(inputString); gradeDistribution[inputInt]++; inputString = inStream.ReadLine(); } inStream.Close(); } File I/O  Streams always need to be closed when they are finished being used.  If they don’t get closed, unexpected results may appear in the files that are being accessed.

52 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng51 Output to Data File  Now that our program knows the distribution of grades from our input file, it would be nice to produce a graph.  Graphs are not simple to produce.  However, we can easily output our data to a file and open it in Excel. With just a few clicks in Excel, a graph can be created.

53 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng52 Output to Data File  What information do I want to put in the file?  What format do I need to use?

54 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng53 Output to Data File  What information do I want to put in the file?  The grade points  The number of students with each grade point.  What format do I need to use?  A file that is tab delimited is easily accessed in Excel. In this case, tab delimited means that the tab character separates entries on any line, i.e. grade_point_average tab number_of_students

55 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng54 Algorithm for Output to File  Run previously developed program.  Start at beginning of grade point array  Print a line in a file that reads:  arrayIndexarrayContents Where arrayIndex is the index of grade point array. arrayContents is the numerical value in that index. arrayIndex and arrayContents are separated by a tab.  Loop through grade point array until all grade points have been written to the file.

56 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng55.. Program to input / create data not shown on this slide.. StreamWriter outStream = new StreamWriter("output.txt"); for (int i = 0; i < gradeDistribution.Length; i++){ outStream.WriteLine(i + "\t" + gradeDistribution[i]); } outStream.Close(); Output to Data File  Run previously developed program.  Start at beginning of grade point array.  Print a line in a file that reads:  arrayIndexarrayContents  Loop through grade point array until all grade points have been written to the file.

57 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng56.. Program to input / create data not shown on this slide.. StreamWriter outStream = new StreamWriter("output.txt"); for (int i = 0; i < gradeDistribution.Length; i++){ outStream.WriteLine(i + "\t" + gradeDistribution[i]); } outStream.Close(); Output to Data File  This declares an output stream.  It creates a file called "output.txt" in the application's working directory.  We can write strings to this output stream in the same way we have used strings in message boxes for example.

58 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng57.. Program to input / create data not shown on this slide.. StreamWriter outStream = new StreamWriter("output.txt"); for (int i = 0; i < gradeDistribution.Length; i++){ outStream.WriteLine(i + "\t" + gradeDistribution[i]); } outStream.Close(); Output to Data File  Run previously developed program.  Start at beginning of grade point array.  Print a line in a file that reads:  arrayIndexarrayContents  Loop through grade point array until all grade points have been written to the file.

59 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng58.. Program to input / create data not shown on this slide.. StreamWriter outStream = new StreamWriter("output.txt"); for (int i = 0; i < gradeDistribution.Length; i++){ outStream.WriteLine(i + "\t" + gradeDistribution[i]); } outStream.Close(); Output to Data File  Run previously developed program.  Start at beginning of grade point array.  Print a line in a file that reads:  arrayIndexarrayContents  Loop through grade point array until all grade points have been written to the file.

60 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng59.. Program to input / create data not shown on this slide.. StreamWriter outStream = new StreamWriter("output.txt"); for (int i = 0; i < gradeDistribution.Length; i++){ outStream.WriteLine(i + "\t" + gradeDistribution[i]); } outStream.Close(); Output to Data File  WriteLine() does all the magic of writing to a file.  It writes a single string to our output file (ends with "\n").  \t is the code for a tab.  "\t" is a string with a tab in it

61 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng60.. Program to input / create data not shown on this slide.. StreamWriter outStream = new StreamWriter("output.txt"); for (int i = 0; i < gradeDistribution.Length; i++){ outStream.WriteLine(i + "\t" + gradeDistribution[i]); } outStream.Close(); Output to Data File  Streams must be closed when we are finished using them.

62 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng61 011 20 32 40 50 61 70 83 91 100 110 121 Sample Output  Output from Sample Input: output.txt

63 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng62 Sample Output  If we open our file in Excel, the following message box will appear:

64 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng63 Sample Output  Excel starts this wizard if plain text files are opened with Excel.  Because of our explicit delimiting of our file with tabs, we can just click finish.

65 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng64 011 20 32 40 50 61 70 83 91 100 110 121 Sample Output And with a few clicks … output.txt

66 © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng65 File I/O  The brilliant part of file I/O like this is that it doesn’t matter how big or small our input file is.  The software will read through the entire file and create sensible output.

67 File Dialog Boxes  Easy to include the normal Windows dialogs for browsing to a specific location to store or retrieve a file  Can add OpenFileDialog or SaveFileDialog by dragging from Toolbox to the design view onto your form  The ShowDialog() method shows the dialog  The FileName property gives the filename © Copyright 2006 David Das, Ryan Lortie, Alan Wassyng66


Download ppt "File Input and Output (I/O) Engineering 1D04, Teaching Session 7."

Similar presentations


Ads by Google