--- description: In this lab, we are going to talk about Binary I/O and how we can manipulate binary files, we should know that every file in your computer is a binary file, but the files that contain binary which represents a text will be considered as text files as they are very common. --- <h1 style='border: none'><center>Programming II Lab 9</center></h1> <h2 style='border: none'><center>Binary I/O</center></h2> <h5><center>The Islamic University of Gaza<br>Engineering Faculty<br>Department of Computer Engineering</center></h5> <h6>Author: Mohammed Nafiz ALMadhoun<span style="float:right">2021/04/17</span></h6> --- <p style='text-align:justify'> In this lab, we are going to talk about Binary I/O and how we can manipulate binary files, we should know that every file in your computer is a binary file, but the files that contain binary which represents a text will be considered as text files as they are very common. </p> ## Viewing Files as Binary You can view any file as a binary file using a `Hex Editor`, which is a program that will display the binary data of a file, you could also see your whole hard drive or ram as a big binary chunk. You could use this [online hex editor](https://hexed.it/), or you could install a program such as [HxD](https://mh-nexus.de/en/hxd/). <center> ![The binary data of an Image](https://i.imgur.com/LSCmetA.png =500x) The binary data of an Image. </center> **Note:** We read binary data as hex because it's easier for us, and programs usually store data as bytes (two hex digits). ## Binary I/O Classes The main classes for reading and writing as binary is `InputStream`, and `OutputStream`, these two abstract classes define the common methods for writing or reading binary data (which could be something other than files). Please read the documentation here: https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html As an example, we will take a look at the `FileOutputStraem` class which will output the binary data to a file. #### Example 1 ```java= import java.io.*; public class Test{ public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("testfile.bin"); fos.write(0x10); fos.write(0x20); fos.write(0x30); fos.close(); } } ``` And here's the file in a hex editor: <center> ![Example 1 Output](https://i.imgur.com/EUxSI67.png =500x) Example 1 Output </center> Now we could use `FileInputStream` to read the data from that file. ## Data Streams So it's useful to write data byte by byte, but sometimes we need something bigger than a byte, something like `int`, or `float`, we could do that by hand but there is an easier way, to use `DataInputStraem` and `DataOutputStream` classes. Please take a look at the documentation here: https://docs.oracle.com/javase/8/docs/api/java/io/DataOutputStream.html https://docs.oracle.com/javase/8/docs/api/java/io/DataInputStream.html ### Example 2 ```java= import java.io.*; public class Test{ public static void main(String[] args) throws IOException { FileOutputStream fos = new FileOutputStream("testfile.bin"); DataOutputStream dos = new DataOutputStream(fos); for (int i=1000000;i<1010000;i++){ dos.writeInt(i); } dos.close(); fos.close(); } } ``` Notice that we've created a `FileOutpuStream`, then we've constructed the `DataOutputStream` using that object because we could write data to something other than a file (e.g. a network packet!). So this file will contain the numbers from `1000000` to `1010000`, and the output size will be `40,000` bytes because each `int` will take four bytes. Now if we created a text file with the same data, it will take about `90,000` bytes. **Writing binary data to a binary file is faster in writing and reading, and take much lower space.** ### Example 3 In this example, we will save and read the data of students using `DataOuptutStream` and `DataInputStream`. #### Student Class ```java= public class Student { private String name; private float gpa; public Student(String name, float gpa) { this.name = name; this.gpa = gpa; } public Student(DataInputStream dataStream) throws IOException{ name = dataStream.readUTF(); gpa = dataStream.readFloat(); } public void saveToStream (DataOutputStream dataStream) throws IOException{ dataStream.writeUTF(name); dataStream.writeFloat(gpa); } public void printDetails(){ System.out.printf("Name: %s%n", name); System.out.printf("GPA : %.2f%n", gpa); } } ``` #### Saving Data ```java= Student[] students = { new Student("Mohammed ALMadhoun", 60), new Student("Test 1", 70.1f), new Student("Test 2", 95.2f), new Student("Test 3", 50.5f), }; try(FileOutputStream fos = new FileOutputStream("students.bin")){ try(DataOutputStream dos = new DataOutputStream(fos)){ dos.writeInt(students.length); // Write the length of the array! for (Student s : students){ s.saveToStream(dos); } } } ``` #### Reading Data ```java= Student[] students = null; try(FileInputStream fis = new FileInputStream("students.bin")){ try(DataInputStream dis = new DataInputStream(fis)){ students = new Student[dis.readInt()]; for (int i=0;i<students.length;i++){ Student s = new Student(dis); s.printDetails(); students[i] = s; } } } ``` ## Object Streams In this section, we will try to use `ObjectOutpuStream`, and `ObjectInputStraem`, which are classes that help us automatically serialize an object, so the previous example will be trivial and it will help you a lot if you want to save the data of a certain object. A serializable class should implement the interface `Serializable`, which will tell the output stream that this class could be serialized. ### Example 4 In this example, we will create the previous example using Object Streams. #### Student Class ```java= public class Student implements Serializable { private String name; private float gpa; public Student(String name, float gpa) { this.name = name; this.gpa = gpa; } public void printDetails(){ System.out.printf("Name: %s%n", name); System.out.printf("GPA : %.2f%n", gpa); } } ``` #### Saving Data ```java= Student[] students = { new Student("Mohammed ALMadhoun", 60), new Student("Test 1", 70.1f), new Student("Test 2", 95.2f), new Student("Test 3", 50.5f), }; try(FileOutputStream fos = new FileOutputStream("students.bin")){ try(ObjectOutputStream oos = new ObjectOutputStream(fos)){ oos.writeObject(students); } } ``` #### Reading Data ```java= Student[] students = null; try(FileInputStream fis = new FileInputStream("students.bin")){ try(ObjectInputStream ois = new ObjectInputStream(fis)){ students = (Student[]) ois.readObject(); for (int i=0;i<students.length;i++){ students[i].printDetails(); } } } ``` ## Random Access File Notice that in the previous examples, we access the data sequentially, but sometimes we need to access the data in a random way (which means we will read whatever address we want). Notice that Random Access File is not an output or input stream, it's not a stream. Notice that the constructor of a RandomAccessFile contains the file path and a mode, the mode specifies if you want to read, read-write, or write to that file. Please take a look at the documentation: https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html ### Example 5 ```java= public class Test{ public static void main(String[] args) throws Exception { try (RandomAccessFile raf = new RandomAccessFile("testfile.bin", "r")){ System.out.println(raf.readInt()); System.out.println(raf.readInt()); raf.seek(4 * 100); System.out.println(raf.readInt()); System.out.println(raf.readInt()); raf.seek(4 * 2); System.out.println(raf.readInt()); System.out.println(raf.readInt()); } } } ``` Notice that we are reading the output file of Example 1. The seek method will move the file pointer to a certain location in the file. **Q: What should be the output of this program?** ## Lab Tasks ### Task 1: Create a Hex Dump program In this task, you should create a program that takes a file name as an argument, and prints the hex dump of it, here is an example of my solution: <center> ![Task 1 Expected Ouptut](https://i.imgur.com/2cUgg91.png =500x) Task 1 Expected Ouptut </center> ### Task 2: Data Recovery program In this task, you should create a data recovery program, which will recover files from the hard disk after deleting it, these programs will search for something called **file signature** which is something that identifies files, then they extract that file from the hard disk binary. Take a look here: https://en.wikipedia.org/wiki/List_of_file_signatures Your program should take a file as input (to avoid destroying your hard drive you will read a file), and output what files it finds. The program should extract PDF files, PDF files will always start with these bytes: `25 50 44 46` And end with these bytes: `25 25 45 4F` Your program should search for this pattern and extract the PDF files from this dump file: https://drive.google.com/file/d/1BaPR6Qg6_Xwjt7C7uQ4Um93bgqO8fqY8/view?usp=sharing This dump file contains 2 pdf files, so your program should output two files! ###### tags: `Programming` `Java` `IUG` <center>End Of Lab 9</center>