《Java黑皮书基础篇第10版》 第17章【习题】

Java语言程序设计 习题第十七章

17.2章节习题

17.1什么是文本文件,什么是二进制文件?可以使用文本编辑器来查看文本文件或者二进制文件吗?

文本文件是字符组成的文件,二进制文件是0和1组成的文件

文本编辑器只能访问文本文件

17.2在Java中如何读取和写入文本数据?什么是流?

读取用Scanner类,写入用PrintWriter类

流就是对象,一个负责写入数据的对象叫做输出流,一个负责读取数据的对象叫做输入流

17.3章节习题

17.3 文本 I/O 与二进制 I/O 的区别是什么?

文本I/O需要编码/解码成二进制,二进制I/O则不需要

17.4 在 Java 中,字符在内存中是如何表示的,在文本文件中是如何表示的?

字符在Java内存中使用Unicode表示,文本文件使用特定的编码规则表示,如ASCII

17.5 如果在一个 ASCII 码文本文件中写人字符串 “ABC”, 那么在文件中存储的是什么值?

0x41 0x42 0x43

17.6 如果在一个ASCII码文本文件中写入字符串"100",那么在文件中存储的是什么值?如果使用二进制I/O写人字节类型数值 100,那么文件中存储的又是什么值?

0x31 0x30 0x30

0x64

17.7 在Java程序中,表示一个字符使用的编码方案是什么?在默认情况下,Windows中文本文件的编码方案是什么?

Java编码方案是Unicode

ASCII

17.4章节习题

17.8 在JavaI/O程序中,为什么必须在方法中声明抛出异常IOExceprion或者在try-catch块中处理该异常?

在二进制I/O类中的几乎所有方法都抛出了必检异常IOException,调用这些方法就需要强制声明或抛出

17.9 为什么总是要求关闭流?如何关闭流?

保证所有的数据都可以被安全的写入,同时释放资源

使用close方法或try-catch-resource

17.10 InputStream的read()方法读取字节,为什么read()方法返回int值而不是字节?找出InputStream和OutputStream中的抽象方法

字节被返回为一个0-255的整数

InputStream中只有read()是抽象方法

OutputStream中只有write(int)是抽象方法

17.11 FilelnputStream类和FileOutputStream类是否相对于继承自的InputStream/OutputStream引入了新方法?如何创建 FilelnputStream 和 FileOutputStream 对象?

没有引入新方法

new FileInputStream()

new FileOutputStream()

17.12 如果试图为一个不存在的文件创建输入流,会发生什么?如果试图为一个已经存在的文件创建输出流,会发生什么?能够将数据追加到一个已存在的文件中吗?

会发生异常FileNotFoundException

文件会被清除而后重新写入

可以

17.13 如何使用java.io.PrintWriter向一个已存在的文本文件中追加数据?

new Formatter(new FileOutputStream("temp.txt", true));

17.14 假如一个文件包含了未指定个数的double值,使用DataOutputStream的writeDouble方法将这些值写入文件。如何编写程序读取所有这些值?如何检测是否到达这个文件的末尾?

使用readDouble()方法

通过捕获并处理EOFException异常来检测到达末尾

17.15 在FileOutputStream上使用writeByte(91)方法后,写人文件的是什么?

91

17.16 在输入流(FilelnputStream和DatalnputStream)中如何判断是否已经到达文件末尾?

read()方法返回-1

17.17 下面的代码有什么错误?

import java.io.*;

public class Test {
  public static void main(String[] args) {
    try (
      FileInputStream fis = new FileInputStream("test.dat"); ) {
    }
    catch (IOException ex) {
      ex.printStackTrace();
    }
    catch (FileNotFoundException ex) {
      ex.printStackTrace();
    }
  }
}

IOException是FileNotFoundException的父类,把父类写在前面就永远无法捕获到子类了

17.18 假设使用默认的ASCII编码方案在Windows上运行程序,在程序结束后,文件t.txt中会有多少个字节?给出每个字节的内容。

public class Test {
  public static void main(String[] args) 
      throws java.io.IOException {
    try (java.io.PrintWriter output = 
        new java.io.PrintWriter("t.txt"); ) {
      output.printf("%s", "1234");
      output.printf("%s", "5678");
      output.close();
    }
  }
}

一个整数占用4个字节,所以题中的两个整数占用8个字节,其中,数字1在ASCII编码下表示为31

31 32 33 34

35 36 37 38

17.19 下面的程序运行完成后,文件t.dat中会有多少个字节?给出每个字节的内容。

import java.io.*;

public class Test {
  public static void main(String[] args) throws IOException {
    try (DataOutputStream output = new DataOutputStream(
        new FileOutputStream("t.dat")); ) {
      output.writeInt(1234);
      output.writeInt(5678);
      output.close();
    }
  }
}

一个整数占用4个字节,所以题中的两个整数占用8个字节

00 00 04 D2

00 00 16 2E

17.20 对于下面这些关于DataOutputStream对象output上的语句,会有多少个字节发送到输出?

output.writeChar('A');
output.writeChars("BC");
output.writeUTF("DEF");

分别是2,4,5个

17.21 使用缓冲流有什么好处?下面的语句是否正确?

BufferedInputStream input1 = new BufferedInputStream(new FileInputStream("t.dat"));

    	DataInputStream input2 = new DataInputStream(new BufferedInputStream(new FileInputStream("t.dat")));

    	DataOutputStream output = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("t.dat")));

没错

17.5章节习题

17.22 程序如何检测一个文件是否已经存在?

exists()方法

17.23 程序如何在读取数据的时候检测是否已经到达文件末尾?

read()方法会返回-1

17.24 程序如何计算从文件读取的字节数?

每当一个字节被读取,numberOfBytesCopied就会+1

17.6章节习题

17.25 使用ObjectOutputStream可以存储什么类型的对象?什么方法可以写人对象?什么方法可以读取对象?从 ObjectlnputStream 读取对象的方法的返回值类型是什么?

任何可序列化的对象

writeObject

readObject

Object类型

17.26 如果序列化两个同样类型的对象,它们占用的空间相同吗?如果不同,举一个例子。

不同

例如两个ArrayLIst,如果他们的元素不同,占用空间也就不同

17.27 是否java.io.Serializable中的任何实例都可以成功地实现序列化?对象的静态变量是否可序列化?如何标记才能避免一个实例变量序列化?

不是

不可以

标记为transient

17.28 可以向ObjectOutputStream中写人一个数组吗?

可以

17.29 在任何情况下,DatalnputStream和DataOutputStream都可以用ObjectlnputStream和ObjectOutputStream替换吗?

是的

17.30 运行下面的代码时,会发生什么?

import java.io.*;
public class Test {
  public static void main(String[] args) throws IOException {
    try ( ObjectOutputStream output = 
        new ObjectOutputStream(new FileOutputStream("object.dat")); ) {
      output.writeObject(new A());
    }
  }
}

class A implements Serializable {
  B b = new B();
}

class B { 
}

NotSerializableException异常

17.7章节习题

17.31 RandomAccessFile 流是否可以读写由 DataOutputStream 创建的数据文件?Random-AccessFile 流是否可以读写对象?

可以

不可以

17.32 为文件 address.dat 创建一个 RandomAccessFile 流,以便更新文件中的学生信息。为文件 address.dat 创建一个 DataOutputStream 流。解释这两条语句之间的差别。

RandomAccessFile raf = new RandomAccessFile("address.dat", "rw");
DataOutputStream outfile = new DataOutputStream(
  new FileWriter("address.dat"));

17.33 如果文件 test.dat 不存在,那么试图编译运行下面的代码会出现什么情况?

import java.io.*;
public class Test { 
  public static void main(String[] args) {
    try ( RandomAccessFile raf =
        new RandomAccessFile("test.dat", "r"); ) {
      int i = raf.readInt();
    }
    catch (IOException ex) {
      System.out.println("IO exception");
    }
  }
}

会引起一个运行时异常

编程练习题

*17.1 (创建一个文本文件)

编写一个程序,如果文件 Exercise17_01.txt 不存在,就创建一个名为 Exercise17_01.txt 的文件。向这个文件追加新数据。使用文本 I/O 将 100 个随机生成的整数写入这个文件。文件中的整数用空格分隔。

import java.io.*;

public class Test {
  public static void main(String[] args) throws java.io.IOException {
	  // true可以保存已有内容,追加新数据
	  FileOutputStream output = new FileOutputStream("Exercise17_01.txt", true);
	  int randomInt;
	  for (int i = 0; i < 100; i++) {
		  // 0-9的ASCII范围是48-57,空格的ASCII表示是32
		  randomInt = (int)(Math.random() * 10 + 48);
		  output.write(randomInt);
		  output.write(32);
	  }
	  output.close();
  }
}

输出结果:

4 6 3 6 9 4 4 1 4 8 8 5 6 1 7 9 1 2 6 5 6 9 2 6 3 6 5 1 9 0 8 2 6 0 2 4 4 9 0 5 7 5 4 1 3 4 9 7 3 9 8 3 1 4 5 1 7 9 0 0 2 8 2 5 9 4 3 7 4 8 1 7 6 8 5 0 1 1 6 3 9 7 6 8 7 4 0 7 7 4 2 4 1 0 6 9 9 1 7 9 
*17.2 (创建二进制数据文件)

编写一个程序,如果文件Exercise17_02.dat不存在,就创建一个名为Exercise17_02.dat的文件。向这个文件追加新数据。使用二进制I/O将100个随机生成的整数写入这个文件中。

import java.io.*;

public class Test {
  public static void main(String[] args) throws java.io.IOException {
	  // true可以保存已有内容,追加新数据
	  FileOutputStream output = new FileOutputStream("Exercise17_02.dat", true);
	  int randomInt;
	  for (int i = 0; i < 100; i++) {
		  // 0-9的ASCII范围是48-57
		  randomInt = (int)(Math.random() * 10 + 48);
		  output.write(randomInt);
	  }
	  output.close();
  }
}

输出结果:

Exercise17_0.dat
*17.3 (对二进制数据文件中的所有整教求和)

假设已经使用 DataOutputStream 中的 writelnt(int) 方法创建了一个名为 Exercise17_03.dat 的二进制数据文件,文件包含数目不确定的整数,编写 一个程序来计算这些整数的总和。

import java.io.*;
import java.util.ArrayList;

public class Test {
  public static void main(String[] args) throws IOException {
	  // 全局变量,list用来储存文件中的所有数字,sum用来储存这些数字的和
	  ArrayList<Integer> list = new ArrayList<>();
	  double sum = 0;
	  
	  // 随机写入一些数据
	  try (DataOutputStream output = new DataOutputStream(new FileOutputStream("Exercise17_03.txt"))) {
		  output.writeInt(50);
		  output.writeInt(32);
		  output.writeInt(88);
	  }
	  
	  // 读取数据,每读取一个数据,就加入到list中,读取完成就捕获异常
	  try (DataInputStream input = new DataInputStream(new FileInputStream("Exercise17_03.txt"))) {
		  while (true)
			  list.add(input.readInt());
	  } catch (EOFException e) {
		  System.out.println("End of File");
	  }
	  
	  // 把读取的数据提取出来加和,算平均值
	  for (int i = 0; i < list.size(); i++)
			sum = sum + list.get(i);
	  System.out.printf("The average is %.2f", sum / list.size());
  }
}

输出结果:

End of File
The average is 56.67
*17.4 (将文本文件转換为UTF格式)

编写一个程序,每次从文本文件中读取多行字符,并将这些行字符以UTF-8字符串格式写入一个二进制文件中。显示文本文件和二进制文件的大小。使用下面的命令运行这个程序:

import java.io.*;
import java.util.ArrayList;

public class Test {
  public static void main(String[] args) throws java.io.IOException {
	  // 全局变量list用来储存每一个UTF的值
	  ArrayList<String> list = new ArrayList<>();
	  // 两个文件的命令行参数
	  File tempFile = new File(args[0]);
	  File finalFile = new File(args[1]);
	  
	  // 先写入文件
	  try (DataOutputStream output = new DataOutputStream(new FileOutputStream(tempFile))) {
		  output.writeUTF("Hello, World1!\n");
		  output.writeUTF("Hello, World2!\n");
		  output.writeUTF("Hello, World3!\n");
		  output.writeUTF("Hello, World4!\n");
		  output.writeUTF("Hello, World5!");
	  }
	  
	  // 读取写入的文件
	  try (DataInputStream input = new DataInputStream(new FileInputStream(tempFile))) {
		  while (true)
			  list.add(input.readUTF());
	  } catch (EOFException e) {
		  System.out.println("End of File");
	  }
	  
	  // 把读取的内容写入到新的文件
	  try (DataOutputStream output = new DataOutputStream(new FileOutputStream(finalFile))) {
		  for (int i = 0; i < list.size(); i++) {
			  output.writeUTF(list.get(i));
		  }
	  }
	  
	  // 打印两个文件大小
	  System.out.println("The original file size is " + tempFile.length() + " bytes");
	  System.out.println("The final file size is " + finalFile.length() + " bytes");
  }
}

输出结果:

(base) kevinwang@KevindeMacBook-Pro ~ % java /Users/kevinwang/Eclipse/test/src/Test.java /Users/kevinwang/Eclipse/test/temp.txt /Users/kevinwang/Eclipse/test/final.dat
End of File
The original file size is 84 bytes
The final file size is 84 bytes
*17.5 (将对象和数组存储在文件中)

编写一个程序,向一个名为Exercise17_05.dat的文件中存储一个含5个int值1、2、3、4、5的数组,和一个表示当前时间的Date对象,以及一个double值5.5

import java.util.Date;
import java.io.*;

public class Test {
	public static void main(String[] args) {
		// 初始化3个要求写入的变量
		int[] numbers = {1, 2, 3, 4, 5};
		Date date = new Date();
		Double doubleNum = 5.5;
		
		// 写入这3个变量
		try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("Exercise17_05.dat"))) {
			output.writeObject(numbers);
			output.writeObject(date);
			output.writeObject(doubleNum);
		} catch (IOException e) {
			System.out.println("IOException");
		}
		
		// 读取并打印这3个变量
		try (ObjectInputStream input = new ObjectInputStream(new FileInputStream("Exercise17_05.dat"))) {
			int[] newNumbers = (int[])input.readObject();
			Date newDate = (Date)input.readObject();
			Double newDoubleNum = (Double)input.readObject();
			
			for (int i = 0; i < newNumbers.length; i++) {
				System.out.print(newNumbers[i] + " ");
			}
			System.out.println();
			System.out.println(newDate);
			System.out.println(newDoubleNum);
		} catch (IOException e) {
			System.out.println("IOException");
		} catch (ClassNotFoundException e) {
			System.out.println("ClassNotFoundException");
		}
 	}
}

输出结果:

1 2 3 4 5 
Sat Jun 17 10:08:45 BST 2023
5.5
*17.6 (存储 Loan 对象)

在程序清单10-2中的类 Loan 没有实现 Serializable,改写类 Loan 使之实现 Serializable。编写程序创建 5 个 Loan 对象,并且将它们存储在一个名为Excrcise17_06.dat的文件中

Test.java

import java.io.*;

public class Test {
	public static void main(String[] args) {
		// 初始化5个Loan对象
		Loan loan1 = new Loan(3.5, 5, 10000);
		Loan loan2 = new Loan(4, 5, 20000);
		Loan loan3 = new Loan(4.5, 5, 30000);
		Loan loan4 = new Loan(5, 5, 40000);
		Loan loan5 = new Loan(5.5, 5, 50000);
		
		// 写入这5个对象
		try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("Exercise17_06.dat"))) {
			output.writeObject(loan1);
			output.writeObject(loan2);
			output.writeObject(loan3);
			output.writeObject(loan4);
			output.writeObject(loan5);
		} catch (IOException e) {
			System.out.println("IOException");
		}
		
		// 读取并打印这5个对象
		try (ObjectInputStream input = new ObjectInputStream(new FileInputStream("Exercise17_06.dat"))) {
			Loan newLoan1 = (Loan)input.readObject();
			Loan newLoan2 = (Loan)input.readObject();
			Loan newLoan3 = (Loan)input.readObject();
			Loan newLoan4 = (Loan)input.readObject();
			Loan newLoan5 = (Loan)input.readObject();
			
			System.out.println(newLoan1);
			System.out.println(newLoan2);
			System.out.println(newLoan3);
			System.out.println(newLoan4);
			System.out.println(newLoan5);
		} catch (IOException e) {
			System.out.println("IOException");
		} catch (ClassNotFoundException e) {
			System.out.println("ClassNotFoundException");
		}
 	}
}

Loan.java

public class Loan implements java.io.Serializable{
  private double annualInterestRate;
  private int numberOfYears;
  private double loanAmount;
  private java.util.Date loanDate;

  /** Default constructor */
  public Loan() {
    this(2.5, 1, 1000);
  }

  /** Construct a loan with specified annual interest rate,
      number of years, and loan amount
    */
  public Loan(double annualInterestRate, int numberOfYears,
      double loanAmount) {
    this.annualInterestRate = annualInterestRate;
    this.numberOfYears = numberOfYears;
    this.loanAmount = loanAmount;
    loanDate = new java.util.Date();
  }

  /** Return annualInterestRate */
  public double getAnnualInterestRate() {
    return annualInterestRate;
  }

  /** Set a new annualInterestRate */
  public void setAnnualInterestRate(double annualInterestRate) {
    this.annualInterestRate = annualInterestRate;
  }

  /** Return numberOfYears */
  public int getNumberOfYears() {
    return numberOfYears;
  }

  /** Set a new numberOfYears */
  public void setNumberOfYears(int numberOfYears) {
    this.numberOfYears = numberOfYears;
  }

  /** Return loanAmount */
  public double getLoanAmount() {
    return loanAmount;
  }

  /** Set a newloanAmount */
  public void setLoanAmount(double loanAmount) {
    this.loanAmount = loanAmount;
  }

  /** Find monthly payment */
  public double getMonthlyPayment() {
    double monthlyInterestRate = annualInterestRate / 1200;
    double monthlyPayment = loanAmount * monthlyInterestRate / (1 -
      (1 / Math.pow(1 + monthlyInterestRate, numberOfYears * 12)));
    return monthlyPayment;    
  }

  /** Find total payment */
  public double getTotalPayment() {
    double totalPayment = getMonthlyPayment() * numberOfYears * 12;
    return totalPayment;    
  }

  /** Return loan date */
  public java.util.Date getLoanDate() {
    return loanDate;
  }
  
  public String toString() {
	  return "Your loan interest is " + getAnnualInterestRate() + ", duration is " + getNumberOfYears() + ", and amount is " + getLoanAmount();
  }
}

输出结果:

Your loan interest is 3.5, duration is 5, and amount is 10000.0
Your loan interest is 4.0, duration is 5, and amount is 20000.0
Your loan interest is 4.5, duration is 5, and amount is 30000.0
Your loan interest is 5.0, duration is 5, and amount is 40000.0
Your loan interest is 5.5, duration is 5, and amount is 50000.0
*17.7(从文件中恢复对象)

假设已经用ObjectOutputStream创建了一个名为Exercise17_07.dat的文件。这个文件包含Loan对象。在程序清单10-2中的Loan类没有实现Serializable。改写Loan类实现Serializable。编写程序,从文件中读取Loan对象,并且计算总的贷款额。假定文件中Loan对象的个数未知。使用EOFException来结束这个循环。

上一道题给出了Exercise17_06.dat和Loan.java文件,使用他们作为本题文件

import java.io.*;
import java.util.ArrayList;

public class Test {
	public static void main(String[] args) {
		// 全局变量list表示贷款额,sum表示贷款总额
		ArrayList<Double> list = new ArrayList<>();
		double sum = 0;
		
		// 读取这5个对象
		try (ObjectInputStream input = new ObjectInputStream(new FileInputStream("Exercise17_06.dat"))) {
			while (true) {
				list.add(((Loan)input.readObject()).getLoanAmount());
			}
		} catch (EOFException e) {
			System.out.println("End of File");
		} catch (IOException e) {
			System.out.println("IOException");
		} catch (ClassNotFoundException e) {
			System.out.println("ClassNotFoundException");
		}
		
		// 计算总额
		for (int i = 0; i < list.size(); i++) {
			sum = sum + list.get(i);
		}
		System.out.print("Total loan amount is " + sum);
 	}
}

输出结果:

End of File
Total loan amount is 150000.0
*17.8 (更新计数器)

假设要追踪一个程序的运行次数。可以存储一个 int 值来对文件计数。程序每执行一次,计数器就加1。将程序命名为 Exereise17_08,并且将计数器存储在文件 Exercise17_08.dat 中

import java.io.*;

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
    	// 初始化执行次数是0
        int runs = 0;

        File file = new File("Exercise17_08.dat");
        // 如果文件不存在,就意味着文件是第一次执行,所以执行次数是1
        if (!file.exists()) {
            runs = 1;
            writeFile(runs, file);
        // 如果文件已经存在,就去找过去执行的次数,然后+1代表再执行一次
        } else {
            runs = readFile(file);
            writeFile(++runs, file);
        }
        System.out.println("Number of runs: " + runs);
    }

    // 找出执行的次数,Exercise17_08.dat只有1个int数字,而这个数字就代表着执行的次数
    static int readFile(File file) {
        int i = 0;
        try (DataInputStream dIs = new DataInputStream(new FileInputStream(file))) {
            i = dIs.readInt();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return i;

    }

    // 执行1次,把新的次数写入文件
    static void writeFile(int i, File file) {
        try (DataOutputStream das = new DataOutputStream(new FileOutputStream(file))) {
            das.writeInt(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

Number of runs: 33
***17.9 (地址簿)

编写程序用于存储、返回、增加,以及更新如图 17-20 所示的地址薄。使用固定长度的字符串来存储地址中的每个属性。使用随机访问文件来读取和写人一个地址。假设姓名、街道、城市、州以及邮政编码的长度分别是32、32、20、2、5字节

package com.example.demo;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class Exercise17_09 extends Application {
    static String[] packageParts = Exercise17_09.class.getPackage().getName().split("\\.");
    final static String path = packageParts[0] + File.separator + packageParts[1] + File.separator + "address_store" +
            ".dat";
    static File storage;

    static int pointer;
    static List<String> addresses = new ArrayList<>();

    static final int NAME_SIZE = 32;
    static final int STREET_SIZE = 32;
    static final int CITY_SIZE = 20;
    static final int STATE_SIZE = 2;
    static final int ZIP_SIZE = 5;
    static final int TOTAL = 91;

    static TextField nameField;
    static TextField streetField;
    static TextField cityField;
    static TextField stateField;
    static TextField zipField;

    @Override
    public void start(Stage primaryStage) {
        storage = new File(path);
        String s = retrieve(storage);
        initAddressList(s);

        VBox rootBox = initUserInterface();
        Scene scene = new Scene(rootBox, 500, 170);

        primaryStage.setTitle("Exercise-17.09");
        primaryStage.setScene(scene);
        primaryStage.setOnCloseRequest(event -> {
            System.out.println("Window close requested, saving Addresses now...");
            try {
                store(storage);
            } catch (IOException ioException) {
                ioException.printStackTrace();
                displayError("Something went wrong storing the address file. Please check the log messages in the " +
                        "console output.");
            }
        });
        primaryStage.setOnShowing(event -> {
            if (addresses.size() > 0) {
                setCurrentAddress(addresses.get(0));
            }
        });
        primaryStage.show();
    }

    private void initAddressList(String s) {
        int len = s.length();
        while (len > 0) {
            len = len - TOTAL;
            addresses.add(s.substring(len));
            s = s.substring(0, len);
        }
    }

    private VBox initUserInterface() {
        VBox showBox = new VBox(5.0);

        Label name = new Label("Name");
        Exercise17_09.nameField = new TextField();

        Exercise17_09.nameField.setMinWidth(400);
        HBox topBox = new HBox(name, Exercise17_09.nameField);
        topBox.setSpacing(10);
        topBox.setPadding(new Insets(5, 5, 5, 5));

        showBox.getChildren().add(topBox);

        Label street = new Label("Street");
        Exercise17_09.streetField = new TextField();

        Exercise17_09.streetField.setMinWidth(400);
        HBox midBox = new HBox(street, Exercise17_09.streetField);
        midBox.setSpacing(10);
        midBox.setPadding(new Insets(5, 5, 5, 5));

        showBox.getChildren().add(midBox);

        Label city = new Label("City");
        Exercise17_09.cityField = new TextField();

        HBox h1 = new HBox(city, Exercise17_09.cityField);
        h1.setPadding(new Insets(2, 2, 2, 2));
        h1.setSpacing(10);

        Label state = new Label("State");
        Exercise17_09.stateField = new TextField();

        Exercise17_09.stateField.setMaxWidth(45);

        HBox h2 = new HBox(state, Exercise17_09.stateField);
        h2.setPadding(new Insets(2, 2, 2, 2));
        h2.setSpacing(10);

        Label zip = new Label("Zip");
        Exercise17_09.zipField = new TextField();

        Exercise17_09.zipField.setMaxWidth(65);
        HBox h3 = new HBox(zip, Exercise17_09.zipField);
        h3.setPadding(new Insets(2, 2, 2, 2));
        h3.setSpacing(10);

        HBox bottomBox = new HBox(h1, h2, h3);
        bottomBox.setAlignment(Pos.CENTER);

        showBox.getChildren().add(bottomBox);

        Button addButton = new Button("Add");
        addButton.setOnAction(event -> add());

        Button firstButton = new Button("First");
        firstButton.setOnAction(event -> first());

        Button nextButton = new Button("Next");
        nextButton.setOnAction(event -> next());

        Button previousButton = new Button("Previous");
        previousButton.setOnAction(event -> previous());

        Button lastButton = new Button("Last");
        lastButton.setOnAction(event -> last());

        Button updateButton = new Button("Update");
        updateButton.setOnAction(event -> update(pointer));
        HBox buttonBox = new HBox(addButton, firstButton, nextButton, previousButton, lastButton, updateButton);
        buttonBox.setSpacing(10.0);
        buttonBox.setAlignment(Pos.CENTER);

        showBox.getChildren().add(buttonBox);

        return showBox;
    }

    static void displayError(String error) {
        Alert alert = new Alert(Alert.AlertType.ERROR);
        alert.setContentText(error);
        alert.show();
    }

    private void add() {
        System.out.println(getAddressString());
        addresses.add(getAddressString());
    }

    private void first() {
        if (addresses.size() > 0) {
            pointer = 0;
            setCurrentAddress(addresses.get(pointer));
        } else {
            displayError("Create an address to add to the list.");
        }

    }

    private void next() {
        if (pointer < addresses.size() - 1) {
            ++pointer;
            setCurrentAddress(addresses.get(pointer));
        } else {
            displayError("End of address list reached.");
        }

    }

    private void previous() {
        if (pointer > 0) {
            --pointer;
            setCurrentAddress(addresses.get(pointer));
        } else {
            displayError("Beginning of address list reached.");

        }
    }

    private void last() {
        setCurrentAddress(addresses.get(addresses.size() - 1));
    }

    private void setCurrentAddress(String fixLenStr) {
        Exercise17_09.nameField.setText(fixLenStr.substring(0, 32));
        Exercise17_09.streetField.setText(fixLenStr.substring(32, 64));
        Exercise17_09.cityField.setText(fixLenStr.substring(64, 84));
        Exercise17_09.stateField.setText(fixLenStr.substring(84, 86));
        Exercise17_09.zipField.setText(fixLenStr.substring(86));

    }

    private void update(int pointer) {
        addresses.set(pointer, getAddressString());
        displayInfo("Updated the address!");

    }

    private void displayInfo(String updated) {
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setContentText(updated);
        alert.show();
    }

    protected void store(File f) throws IOException {
        boolean b = false;
        if (!f.exists()) {
            try {
                b = f.createNewFile();
            } catch (Exception e) {
                try {
                    f.setWritable(true);
                    b = f.createNewFile();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }

        try {
            RandomAccessFile raf = new RandomAccessFile(f, "rw");
            for (String address : addresses) {
                raf.writeUTF(address);
            }

            raf.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    protected String retrieve(File file) {
        String read = "";
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                displayError("File does note exist and encountered an error while creating it.");
            }
        } else {
            try {
                RandomAccessFile raf = new RandomAccessFile(file, "r");
                while (true) {
                    read = read.concat(raf.readUTF());
                }

            } catch (EOFException eof) {
                System.out.println("End of File reached!");
                return read;
            } catch (IOException ioException) {
                displayError(ioException.getMessage());
            }
        }
        return read;
    }

    protected String getAddressString() {
        String address = "";
        String name = nameField.getText();

        for (int i = name.length(); i < NAME_SIZE; i++) {
            name += " ";
        }
        address += name;

        String street = streetField.getText();
        for (int i = street.length(); i < STREET_SIZE; i++) {
            street += " ";
        }
        address += street;

        String city = cityField.getText();
        for (int i = city.length(); i < CITY_SIZE; i++) {
            city += " ";
        }
        address += city;

        String state = stateField.getText();
        for (int i = state.length(); i < STATE_SIZE; i++) {
            state += " ";
        }
        address += state;

        String zip = zipField.getText();

        for (int i = zip.length(); i < ZIP_SIZE; i++) {
            zip += " ";
        }
        address += zip;

        return address;
    }
}

输出结果:

《Java黑皮书基础篇第10版》 第17章【习题】_第1张图片

*17.10 (分割文件)

假设希望在 CD-R 上备份一个大文件(例如,一个 10GB 的 AVI文件)。可以将该文件分割为几个小一些的片段,然后独立备份这些小片段。编写一个工具程序,使用下面的命令将一个大文件分割为小一些的文件:

java Exercisel7_10 SourceFile numberOfPieces

这个命令创建文件 SourceFile.1, SourceFile.2, •••, SourceFile.n, 这里的n是 numberOfPieces 而输出文件的大小基本相同。

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
    	String file = args[0];
    	int pieces = Integer.parseInt(args[1]);
    	// 因为要输出给n个文件片段,这里用一个index来表示文件片段的文件名,第一个文件名是1
    	int fileIndex = 1;
    	
    	// 读取大文件的所有字符,并平均分配给若干文件片段
    	try (RandomAccessFile ral = new RandomAccessFile(file, "r")) {
    		// 大文件的字节数
    		long totalLength = ral.length();
    		// 根据指定的片段个数,算出每个片段应该包含的字节数,+1是为了避免大文件的最后几个字符无法写入文件片段
    		long singleLength = totalLength / pieces + 1;
    		// 一会会用到read(b: byte[])方法,先声明一下参数
    		byte[] buffer;
    		
    		// 因为初始化buffer的时候需要指定大小,这个大小是用int形式的数字表示的,如果它超过了int能表示的最大范围,程序就会报错了
    		if (singleLength < Integer.MAX_VALUE && singleLength > Integer.MIN_VALUE) {
    			// 根据片段大小,初始化buffer数组
    			buffer = new byte[(int)singleLength];
    			// 把singleLength大小的字节写入buffer数组,同时把写入的字节长度保存到bytesRead中,这个方法默认,如果文件中剩余的字节大于等于buffer数据长度,就写入buffer数组长度个字节,如果小于,就直接写完剩下所有的
    			int bytesRead = ral.read(buffer);
    			// 如果读取没有结束
    			while (bytesRead != -1) {
    				// 一个一个的把字节写入文件片段
    				for (int i = 0; i < bytesRead; i++) {
    					// 使用fileIndex表示文件名的序列号
    					DataOutputStream output = new DataOutputStream(new FileOutputStream("split" + fileIndex + ".txt", true));
    					output.writeChar((char)buffer[i]);
    					output.close();
    			    }
    				bytesRead = ral.read(buffer);
    				// 写完一个片段,就更换下一个片段
    				fileIndex++;
    			}
    		// 超出范围,程序异常退出
    		} else {
    			System.out.println("Casting Out Of Bound");
    			System.exit(1);
    		}
    		
    		// 提示程序运行成功
    		// 我的mac zsh命令行,如果字符串没有换行符,会自己加上一个%,所以我这里打印println换行,就不会有%了
    		System.out.println("Done!");
    	}
    }
}

输出结果:

(base) kevinwang@KevindeMacBook-Pro ~ % java /Users/kevinwang/Eclipse/test/src/Test.java /Users/kevinwang/Eclipse/test/Exercise17_10.txt 4
Done!

《Java黑皮书基础篇第10版》 第17章【习题】_第2张图片

**17.11 (带GUI的分割文件工具)

改写练习题17.10使之带有GUI,如图17-21a所示。

package com.example.demo;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;

import java.io.*;

public class Test extends Application {
    @Override
    public void start(Stage pStage) {
        VBox vBox = new VBox();

        Text text = new Text("If you split a file into 3 smaller files,\nthe 3 smaller files are split1.txt, split2.txt, split3.txt.");

        GridPane gridPane = new GridPane();
        Text hit1 = new Text("Enter a file: ");
        TextField fileNameField = new TextField();
        Text hit2 = new Text("Specify the number of smaller files: ");
        TextField numPiecesField = new TextField();
        gridPane.add(hit1, 0, 0);
        gridPane.add(fileNameField, 1, 0);
        gridPane.add(hit2, 0, 1);
        gridPane.add(numPiecesField, 1, 1);

        Button button = new Button("Start");

        vBox.getChildren().addAll(text, gridPane, button);
        VBox.setMargin(text, new Insets(5, 5, 5, 5));
        VBox.setMargin(gridPane, new Insets(5, 5, 5, 5));
        VBox.setMargin(button, new Insets(5, 5, 5, 130));

        button.setOnAction(e -> {
            try {
                String[] args = new String[2];
                args[0] = fileNameField.getText();
                args[1] = numPiecesField.getText();
                split(args);
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        });

        Scene scene = new Scene(vBox, 300, 150);
        pStage.setTitle("Split Test");
        pStage.setScene(scene);
        pStage.show();
    }

    public static void split(String[] args) throws IOException {
        String file = args[0];
        int pieces = Integer.parseInt(args[1]);
        // 因为要输出给n个文件片段,这里用一个index来表示文件片段的文件名,第一个文件名是1
        int fileIndex = 1;

        // 读取大文件的所有字符,并平均分配给若干文件片段
        try (RandomAccessFile ral = new RandomAccessFile(file, "r")) {
            // 大文件的字节数
            long totalLength = ral.length();
            // 根据指定的片段个数,算出每个片段应该包含的字节数,+1是为了避免大文件的最后几个字符无法写入文件片段
            long singleLength = totalLength / pieces + 1;
            // 一会会用到read(b: byte[])方法,先声明一下参数
            byte[] buffer;

            // 因为初始化buffer的时候需要指定大小,这个大小是用int形式的数字表示的,如果它超过了int能表示的最大范围,程序就会报错了
            if (singleLength < Integer.MAX_VALUE && singleLength > Integer.MIN_VALUE) {
                // 根据片段大小,初始化buffer数组
                buffer = new byte[(int)singleLength];
                // 把singleLength大小的字节写入buffer数组,同时把写入的字节长度保存到bytesRead中,这个方法默认,如果文件中剩余的字节大于等于buffer数据长度,就写入buffer数组长度个字节,如果小于,就直接写完剩下所有的
                int bytesRead = ral.read(buffer);
                // 如果读取没有结束
                while (bytesRead != -1) {
                    // 一个一个的把字节写入文件片段
                    for (int i = 0; i < bytesRead; i++) {
                        // 使用fileIndex表示文件名的序列号
                        DataOutputStream output = new DataOutputStream(new FileOutputStream("split" + fileIndex + ".txt", true));
                        output.writeChar((char)buffer[i]);
                        output.close();
                    }
                    bytesRead = ral.read(buffer);
                    // 写完一个片段,就更换下一个片段
                    fileIndex++;
                }
                // 超出范围,程序异常退出
            } else {
                System.out.println("Casting Out Of Bound");
                System.exit(1);
            }

            // 提示程序运行成功
            // 我的mac zsh命令行,如果字符串没有换行符,会自己加上一个%,所以我这里打印println换行,就不会有%了
            System.out.println("Done!");
        }
    }
}

输出结果:

《Java黑皮书基础篇第10版》 第17章【习题】_第3张图片

*17.12 (组合文件)

编写一个工具程序,使它能够用下面的命令,将文件组合在一起构成一个新文件:

java Exercisel7_12 SourceFlle1 ... SourceFileN TargetFile

这个命令将 SourceFile1, …, SourceFileN 合并为TargetFile

见17.13

*17.13 (带GUI的组合文件工具)

改写编程练习题17.12使之带有GUI,如图1721b所示

17.12和17.13本质上是一样的,这里直接把17.13GUI画出来

package com.example.demo;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;

import java.io.*;

public class Test extends Application {
    @Override
    public void start(Stage pStage) {
        VBox vBox = new VBox();

        Text text = new Text("If the base file is named temp.txt with 3 pieces,\nsplit1.txt, split2.txt, split3.txt are combined into temp.txt");

        GridPane gridPane = new GridPane();
        Text hit1 = new Text("Enter a file: ");
        TextField fileNameField = new TextField();
        Text hit2 = new Text("Specify the number of smaller files: ");
        TextField numPiecesField = new TextField();
        gridPane.add(hit1, 0, 0);
        gridPane.add(fileNameField, 1, 0);
        gridPane.add(hit2, 0, 1);
        gridPane.add(numPiecesField, 1, 1);

        Button button = new Button("Start");

        vBox.getChildren().addAll(text, gridPane, button);
        VBox.setMargin(text, new Insets(5, 5, 5, 5));
        VBox.setMargin(gridPane, new Insets(5, 5, 5, 5));
        VBox.setMargin(button, new Insets(5, 5, 5, 130));

        button.setOnAction(e -> {
            try {
                String[] args = new String[2];
                args[0] = fileNameField.getText();
                args[1] = numPiecesField.getText();
                combine(args);
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        });

        Scene scene = new Scene(vBox, 320, 150);
        pStage.setTitle("Split Test");
        pStage.setScene(scene);
        pStage.show();
    }

    public static void combine(String[] args) throws IOException {
        String file = args[0];
        int pieces = Integer.parseInt(args[1]);
        // 因为要输出给n个文件片段,这里用一个index来表示文件片段的文件名,第一个文件名是1
        int fileIndex = 1;

        for (; fileIndex <= pieces; fileIndex++) {
            DataOutputStream output = new DataOutputStream(new FileOutputStream(file, true));
            DataInputStream input = new DataInputStream(new FileInputStream("split" + fileIndex + ".txt"));
            int bytes = input.read();
            while (bytes != -1) {
                output.write((char)bytes);
                bytes = input.read();
            }
        }

        System.out.println("Done!");
        }
    }

输出结果:

《Java黑皮书基础篇第10版》 第17章【习题】_第4张图片

17.14 (加密文件)

通过给文件中的每个字节加5来对文件编码。编写一个程序,提示用户输入一个输入文件名和一个输出文件名,然后将输人文件的加密版本存人输出文件。

package com.example.demo;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        // 原始文件
        String originalFile = args[0];
        // 加密文件
        String encryptFile = args[1];

        DataOutputStream output = new DataOutputStream(new FileOutputStream(encryptFile, true));
        DataInputStream input = new DataInputStream(new FileInputStream(originalFile));
        int bytes = input.read();
        while (bytes != -1) {
            output.write((char)(bytes + 5));
            bytes = input.read();
        }

        System.out.println("Done!");
    }
}

输出结果:

(base) kevinwang@KevindeMacBook-Pro demo % java /Users/kevinwang/IDEA/demo/src/main/java/com/example/demo/Test.java /Users/kevinwang/IDEA/demo/origin.txt /Users/kevinwang/IDEA/demo/final.txt
Done!

《Java黑皮书基础篇第10版》 第17章【习题】_第5张图片

17.15 (解密文件)

假设文件是用编程练习题17.14中的编码方案加密的。编写一个程序,解码这个加密文件。程序应该提示用户输入一个输入文件名和一个输出文件名,然后将输人文件的解密版本存人输出文件。

package com.example.demo;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        // 密码文件
        String passwordFile = args[0];
        // 解密以后的文件
        String afterDecryptionFile = args[1];

        DataOutputStream output = new DataOutputStream(new FileOutputStream(afterDecryptionFile, true));
        DataInputStream input = new DataInputStream(new FileInputStream(passwordFile));
        int bytes = input.read();
        while (bytes != -1) {
            output.write((char)(bytes - 5));
            bytes = input.read();
        }

        System.out.println("Done!");
    }
}

输出结果:

(base) kevinwang@KevindeMacBook-Pro demo % java /Users/kevinwang/IDEA/demo/src/main/java/com/example/demo/Test.java /Users/kevinwang/IDEA/demo/password.txt /Users/kevinwang/IDEA/demo/decryption.txt
Done!

《Java黑皮书基础篇第10版》 第17章【习题】_第6张图片

17.16 (字符的頻率)

编写一个程序,提示用户输入一个 ASCII 文本文件名,然后显示文件中每个字符出现的频率。

package com.example.demo;

import java.io.File;
import java.io.FileInputStream;
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("Enter the name of an ASCII text file: ");
        String fileName = in.next();
        if (!fileName.isEmpty()) {
            File file = new File(fileName);
            int totalChars = 0;
            int totalLines = 1;
            try (FileInputStream fileInputStream = new FileInputStream(file)) {
                int nextChar;
                while ((nextChar = fileInputStream.read()) != -1) {
                    char ch = (char) nextChar;
                    if (ch != ' ') {
                        totalChars++;
                    }
                    if (ch == '\n') {
                        totalLines++;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println("There are " + totalChars + " total characters in the file.");
            System.out.println("There are " + totalLines + " total lines in the file.");
            System.out.println("So the file has an average of " + totalChars / totalLines + " characters per line.");
        } else {
            System.out.println("Please enter a file name in the prompt...");
            System.exit(1);
        }
        in.close();
    }
}

输出结果:

Enter the name of an ASCII text file: Exercise17_16.txt
There are 92 total characters in the file.
There are 1 total lines in the file.
So the file has an average of 92 characters per line.
**17.17(BitOutputStream)

实现一个名为BitOutputStream的类,如图17-22所示,将比特写入一个输出流。方法writeBit(char bit)存储一个字节变量形式的比特。创建一个BitOutputStream时,该字节是空的。在调用writeBit(“1”)之后,这个字节就变成 00000001。在调用writeBit(“0101”)之后,这个字节就变成00010101。前三个字节还没有填充。当字节填满后,就发送到输出流。现在,字节重置为空。必须调用close()方法关闭这个流。如果这个字节非空也非满,close()方法就会先填充0以使字节的8个比特都被填满,然后输出字节并关闭这个流。可以参见编辑练习题5.44得到提示。编写一个测试程序,将比特010000100100001001101发送给一个名为Exercise17_17.dat的文件

Test.java

package com.example.demo;

import java.io.File;
import java.io.IOException;

public class Test {
    public static void main(String[] args) {
        File testFile = new File("Exercise17_17.txt");
        try {
            BitOutputStream bitOutputStream = new BitOutputStream(testFile);
            bitOutputStream.writeBit("010000100100001001101");
            bitOutputStream.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }
}

BitOutputStream.java

package com.example.demo;

import java.io.*;

public class BitOutputStream implements Closeable {
    private int value;
    private FileOutputStream fileOutputStream;
    private int posCounter = 0;
    private File file;

    public BitOutputStream(File file) throws FileNotFoundException {
        this.file = file;
        fileOutputStream = new FileOutputStream(file);
    }

    public void writeBit(char bit) {
        if (isValid(bit)) {
            posCounter++;
            value = value << 1; // Shift over bit values to left by one. For Example: (00001111 << 1) = 00011110
            if (bit == '1') { // If bit is '1' perform logic to change right-most 0 to 1
                value = value | 1; // Use bit masking to turn the last bit on.
            }
            // Check if value is full (when posCounter is 8), if so, write to file and reset aByte
            if (posCounter == 8) { // value is full (Bit at position 8 is always 0 (non-negative))
                try {
                    System.out.println("Byte value is full, writing to FileOutputStream: " + Integer.toBinaryString(value));
                    fileOutputStream.write(value); // Write to OutputStream
                    posCounter = 0; // Reset capacity counter to zero
                    value = 0;
                } catch (IOException ioException) {
                    System.out.println("Error: internal fileOutputStream through exception. Please check you are using " +
                            "correct 'file' parameter.");
                    ioException.printStackTrace();
                }
            }

            // Print the value result formatted as a String for clarity
//            System.out.println("bit is " + bit + " -> value is " + Integer.toBinaryString(aByte));
        } else {
            throw new IllegalArgumentException("writeBit method only excepts char parameters of '0' or '1' ");
        }
    }

    private boolean isValid(char bit) {
        return bit == '0' ||
                bit == '1' ||
                bit == '\n' ||
                bit == '\t';
    }

    public void writeBit(String bit) {
        for (int i = 0; i < bit.length(); i++) {
            writeBit(bit.charAt(i));
        }
    }

    @Override
    public void close() throws IOException {
        if (posCounter > 0) {
            int shift = 8 - posCounter;
            value = value << shift;
            fileOutputStream.write(value);
            System.out.println("Filling rest of the byte value with zeros and writing to FileOutputStream:  " + Integer.toBinaryString(value));
        }
        fileOutputStream.close();
    }
}

输出结果:

Byte value is full, writing to FileOutputStream: 1000010
Byte value is full, writing to FileOutputStream: 1000010
Filling rest of the byte value with zeros and writing to FileOutputStream:  1101000
*17.18 (查看比特)

编写下面的方法,用于显示一个整数的最后一个字节的比特表示。可以参见编程练习题 5.44 获得提示。编写一个程序,提示用户输入一个文件名,从文件读取字节,然后显示每个字节的二进制表示形式。

package com.example.demo;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Scanner;

public class Test {
    static BufferedInputStream bufferedInputStream;
    static ArrayList<String> strBytes;

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        strBytes = new ArrayList<>();
        System.out.println("Enter a filename: ");
        String fileName = in.nextLine().trim();
        try {
            bufferedInputStream = new BufferedInputStream(new FileInputStream(fileName));
            int b;
            while ((b = bufferedInputStream.read()) != -1) {
                String val = getBits(b);
                strBytes.add(val);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("The contents of the file, converted to bytes are: ");
        System.out.println(strBytes.toString());

        in.close();
    }

    public static String getBits(int value) {
        StringBuilder bits = new StringBuilder();
        long i;
        for (i = 128; i > 0; i /= 2) {
            bits.append((value & i) != 0 ? "1" : "0");
        }
        return bits.toString();
    }
}

输出结果:

Enter a filename: 
Exercise17_17.txt
The contents of the file, converted to bytes are: 
[01000010, 01000010, 01101000]
*17.19 (查看十六进制)

编写一个程序,提示用户输入文件名,从文件读取字节,然后显示每个字节的十六进制表示形式。

package com.example.demo;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Scanner;

public class Test {
    static BufferedInputStream bufferedInputStream;
    static ArrayList<String> strBytes;

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        strBytes = new ArrayList<>();
        System.out.println("Enter a filename: ");
        String fileName = in.nextLine().trim();
        try {
            bufferedInputStream = new BufferedInputStream(new FileInputStream(fileName));
            int b;
            while ((b = bufferedInputStream.read()) != -1) {
                String val = getBits(b);
                System.out.println("Read byte value = " + val);
                int asInteger = Integer.parseInt(val, 2);
                strBytes.add(Integer.toHexString(asInteger).toUpperCase());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("The byte contents of the file, converted to hex are: ");
        System.out.println(strBytes.toString());
    }

    public static String getBits(int value) {
        StringBuilder bits = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            bits.insert(0, (value & 1));
            value >>= 1;
        }
        return bits.toString();
    }
}

输出结果:

Enter a filename: 
Exercise17_17.txt
Read byte value = 01000010
Read byte value = 01000010
Read byte value = 01101000
The byte contents of the file, converted to hex are: 
[42, 42, 68]
**17.20 (二进制编辑器)

编写一个GUI应用程序,让用户在文本域输入一个文件名,然后单击回车键,在文本区域显示它的二进制表示形式。用户也可以修改这个二进制代码,然后将它回存到这个文件中,如图17-23a所示。

package com.example.demo;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.*;

public class Test extends Application {
    private static String filePath = null;
    private SimpleStringProperty editBoxString = new SimpleStringProperty();

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox mainBox = new VBox(5);
        HBox topBox = new HBox(5);

        TextField textField = new TextField();
        Label label = new Label("Enter a file: ");
        topBox.getChildren().add(label);
        topBox.getChildren().add(textField);
        mainBox.getChildren().add(topBox);

        HBox.setHgrow(textField, Priority.ALWAYS);
        HBox.setMargin(textField, new Insets(5, 5, 5, 1));
        HBox.setMargin(label, new Insets(9, 1, 5, 5));

        TextArea editBox = new TextArea();
        editBox.setWrapText(true);
        editBoxString.bindBidirectional(editBox.textProperty());

        mainBox.getChildren().add(editBox);

        HBox buttonBox = new HBox(5);
        Button saveButton = new Button("Save the change");
        saveButton.setOnAction(e -> {
            filePath = textField.getText().trim();
            try {
                writeFile(editBoxString.get(), new File(filePath));
                editBox.clear();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        });
        buttonBox.setAlignment(Pos.CENTER);
        buttonBox.getChildren().add(saveButton);
        saveButton.setCenterShape(true);

        mainBox.getChildren().add(buttonBox);
        VBox.setMargin(buttonBox, new Insets(5, 5, 5, 5));

        Scene scene = new Scene(mainBox, 600, 300);
        scene.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.ENTER) {
                try {
                    filePath = textField.getText().trim();

                    String bytes = readFile(new File(filePath));
                    editBoxString.set(bytes);

                } catch (IOException ioException) {
                    System.out.println("FilePath: " + filePath);
                    ioException.printStackTrace();
                }
            }
        });

        primaryStage.setScene(scene);
        primaryStage.setTitle(getClass().getName());
        primaryStage.show();

    }

    private void writeFile(String text, File file) throws IOException {
        BitOutputStream bitOutputStream = new BitOutputStream(file);
        bitOutputStream.writeBit(text);

    }

    private String readFile(File file) throws IOException {
        BitInputStream bitOutputStream = new BitInputStream(file);
        return bitOutputStream.readAllBytes();
    }

    public static class BitOutputStream implements Closeable {
        private FileOutputStream output;
        private int value;
        private int count = 0;
        private int mask = 1;

        public BitOutputStream(File file) throws IOException {
            output = new FileOutputStream(file);
        }

        public void writeBit(char bit) throws IOException {
            count++;
            value = value << 1;
            if (bit == '1')
                value = value | mask;
            if (count == 8) {
                output.write(value);
                count = 0;
            }
        }

        public void writeBit(String bitString) throws IOException {
            for (int i = 0; i < bitString.length(); i++)
                writeBit(bitString.charAt(i));
        }

        @Override
        public void close() throws IOException {
            if (count > 0) {
                value = value << (8 - count);
                output.write(value);
            }
            output.close();
        }

        void t() {
        }

    }

    public static class BitInputStream implements Closeable {
        private FileInputStream input;
        private String stringValue;
        private static final int EOF = -1;

        public BitInputStream(File file) throws IOException {
            input = new FileInputStream(file);
        }

        private String readAllBytes() throws IOException {
            stringValue = "";
            int intValue;
            while ((intValue = input.read()) != EOF) {
                stringValue += readBits(intValue);
            }
            input.close();
            return stringValue;

        }

        private String readBits(int value) {
            String bits = "";
            int mask = 1;
            for (int i = 7; i >= 0; i--) {
                int temp = value >> i;
                int bit = temp & mask;
                bits = bits + bit;
            }
            return bits;
        }

        @Override
        public void close() throws IOException {
            input.close();
        }
    }
}

输出结果:

《Java黑皮书基础篇第10版》 第17章【习题】_第7张图片

**17.21 (十六进制编辑器)

编写一个 GUI 应用程序,让用户在文本域输入一个文件名,然后按回车键,在文本域显示它的十六进制表达形式。用户也可以修改十六进制代码,然后将它回存到这个文件中,如图17-23b所示。

package com.example.demo;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.*;

public class Test extends Application {
    private static String filePath = null;
    SimpleStringProperty editBoxString = new SimpleStringProperty();

    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox mainBox = new VBox(5);
        HBox topBox = new HBox(5);

        TextField textField = new TextField();
        Label label = new Label("Enter a file: ");
        topBox.getChildren().add(label);
        topBox.getChildren().add(textField);
        mainBox.getChildren().add(topBox);

        HBox.setHgrow(textField, Priority.ALWAYS);
        HBox.setMargin(textField, new Insets(5, 5, 5, 1));
        HBox.setMargin(label, new Insets(9, 1, 5, 5));

        TextArea editBox = new TextArea();
        editBox.setWrapText(true);
        editBoxString.bindBidirectional(editBox.textProperty());

        mainBox.getChildren().add(editBox);

        HBox buttonBox = new HBox(5);
        Button saveButton = new Button("Save the change");
        saveButton.setOnAction(e -> {
            try {
                write(editBoxString.get(), filePath);
                editBox.clear();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        });
        buttonBox.setAlignment(Pos.CENTER);
        buttonBox.getChildren().add(saveButton);
        saveButton.setCenterShape(true);

        mainBox.getChildren().add(buttonBox);
        VBox.setMargin(buttonBox, new Insets(5, 5, 5, 5));

        Scene scene = new Scene(mainBox, 600, 300);
        scene.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.ENTER) {
                try {
                    filePath = textField.getText().trim();
                    String hexValuesInString = read(filePath);
                    editBoxString.set(hexValuesInString);

                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        });
        primaryStage.setScene(scene);
        primaryStage.setTitle(getClass().getName());
        primaryStage.show();

    }

    private void write(String hexValues, String filePath) throws IOException {
        FileOutputStream output = new FileOutputStream(filePath);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output));
        for (int i = 0; i < hexValues.length(); i += 2) {
            char value = (char) ((Character.digit(hexValues.charAt(i), 16) << 4) + Character.digit(hexValues.charAt(i + 1), 16));
            writer.write(value);
        }
        writer.close();
    }

    private String read(String filePath) throws IOException {
        FileInputStream input = new FileInputStream(filePath);
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        String asChars = "";
        int next;
        while ((next = reader.read()) != -1) {
            asChars += String.format("%H", next);
        }
        input.close();
        return asChars;
    }


    public static void main(String[] args) {
        Application.launch(args);
    }
}

输出结果:
《Java黑皮书基础篇第10版》 第17章【习题】_第8张图片

你可能感兴趣的:(java,开发语言)