IO——关于文件内容的读写

读InputStream

1)直接读取(以二进制数据的方式读取,表现在代码中 byte 为单位)

2)文本读取

1 . java.io.lnputstream 输入流

2 .本身是一个抽象类,真正使用过程中要依赖这个抽象的具体实现子类 Filelnputstream(关于文件的输入流 )

3 · 要记得关闭is.close ( )

FileInputStream构造方法

InputStream 只是一个抽象类,要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使用FileInputStream

image-20220708230501400

读取方法

利用InputStream进行二进制数据的直接读取

image-20220708224656169

假设这次接了 0 滴水:

A .是暂时现在没水了? B .以后永远没水了?

除了要接水之外,我们还需要一个明确的信号(说明肯定不会有新的数据了),这个信号用EOS表示

EOS: End Of Stream 在我们的代码中,使用-1去表示

image-20220708230009911

  • abstract int read ( ): 一次读一个字节(一次只接一滴水),返回的是下个字节的数据。当返回值是 EOS(-1)的时候,表示以后永远没有水了

  • int read(byte[ ] b):一次读一批(一次接很多水),需要提前准备好桶(byte[])。返回值是本次接到的水滴总数,n >= 0 && n <= byte.length。 当返回值是EOS(-1),表示,以后永远没有水了

例:将文件完全读完的两种方式。相比较而言,后一种的 IO 次数更少,性能更好。

        System.out.println("一次接一滴水:");
        InputStream is1 = new FileInputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\hello.txt");
        while (true) {
            int data = is1.read();
            if (data == -1) {
                // 所有数据都被读完了
                break;
            }
            // 否则,data 就是我们取到的数据
            byte b = (byte) data;
            System.out.printf("%02x\n", b);
        }
        is1.close();

        System.out.println("使用任意容量大小的桶,分多次接水:");
        InputStream is2 = new FileInputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\hello.txt");
        byte[] buf1 = new byte[5];
        while (true) {
            int n1 = is2.read(buf1);
            // n == 0 只是本次没数据,以后还有
            // n == -1 本次没数据,以后也没数据了
            if (n1 == -1) {
                // 代表数据全部读完
                break;
            }

            for (int i = 0; i < n1; i++) {
                byte b = buf1[i];
                System.out.printf("%02x\n", b);
            }
        }
        is2.close();

附送小知识:

  • windows 上的换行默认是“\r\n” 也写作 CRLF,占两个字符,是用UTF-8的编码写
  • try - with - resources 语法糖:
// 完善写法:
try { 
    Inputstream iS = ... ;
    ...
} catch ( SomeException exc ) {
	...
} finally { 
    if ( is != null ) { 
        is.close(); 
    }
}

// 简化写法:
// 实际在执行时java编译器将下面这段代码完善成上面的样子
*.java
// 语法:try (定义变量= … ) { } 
try ( Inputstream is = ...){
    ...
}catch ( SomeException exc ) {
	...
} //编译的时候自动添加了 is.close()

用途:只要是资源都可用

// 除可用在Inputstream / Outputstream ... 
// 还可用在:JDBC 
try ( Connection c = ... ) { 
    try ( Preparestatement ps = c.preparestatement ( sql ) ) { 
        ...
    }
}

总结:如何利用InputStream进行二进制数据的直接读取:

1)一次读一个字节 2)一次读一批 3)EOS(-1)

文本数据(字符数据)

类型

ASCll 、 Unicode 、 UTF-8 、 GBK ( GB18030 、 GB2312 )

计算机中的数据<->数字(有范围的整数),如:(图像、声音、文字)< -->数字

文字到数字的转换方法——查表法

规定一个数字范围,每个数字都唯一标定一个字符,比如:

image-20220707230202473

如果每个人都定义自己的字符集,实际上没有任何的意义。所以,总是由标准委员会来规定好一个字符集,大家一起遵守!所以才有各种各样的标准字符集的出现!ASCII就是一种字符集

字符集(char set) 和 字符编码规则(char encoding)

字符集ASCII和Unique:

  • ASCll: 表示范围有限 [ 0 ~ 128 ),1个字节,实际使用中就直接使用即可。ASCll 是 Unicode 的子集

  • Unicode: [0 , 2 ^32 ) 约1 千万亿(连emoji都可编码), 4 个字节 。如果真的每个字都用 4 个字节,就有很多空间浪费,因此分成不同的编码规则

    • UTF - 8: 变长的编码规则一个字符占用的空间 1 ~ 4个字节不等。ASCll范围内的,英文仍然是 1 个字节,中文的字一般是 3 个字节。
    • UTF - 32
    • UTF - 16: Char
    • GB (国标)\ GB2312 \ GB18030 \ GBK :中文、英文都是 2 个字节

ASCII可以理解成既是字符集又是编码规则,Unicode占用空间过大,因此在Unicode字符集的基础上分出多个编码规则

乱码问题

编解码过程不匹配导致的问题

image-20220707230122478

二进制数据转化为对应的字符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-plJq4ie1-1657516114381)(img/image-20220708235226909.png)]

offset:bytes的开始下标

charsetName:使用的字符集名,如:”UTF-8”

try (InputStream is1 = new FileInputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\hello.txt")) {
    byte[] buf = new byte[1024];
    int n = is1.read(buf);
    // 将数组中二进制字节用UTF-8编码表示
    String s = new String(buf, 0, n, "UTF-8");
    System.out.println(s);
} catch (Exception e) {
    e.printStackTrace();
}

利用 Scanner 进行字符读取

构造方法

image-20220709005139243

image-20220708235123261

System.in的类型其实就是一个InputStream(终端,如:键盘)

image-20220707230526676

try (InputStream is2 = new FileInputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\hello.txt")) {
    try (Scanner scanner = new Scanner(is2, "UTF-8")) {
        // 一行一行读
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();   // 默认去掉了换行符
            System.out.println("|" + line + "|");
        }
    }
} catch (IOException exc) {
    exc.printStackTrace();
}

读取键盘的输入数据

Scanner scanner = new Scanner(System.in);
System.out.println("请输入数据:");
// 直接敲回车:这次没有输入 但是以后还会有
// 永远停止(EOS):  IDEA:Ctrl + D、CMD:Ctrl + Z
while (scanner.hasNext()) { 
    String word = scanner.next();
    System.out.println("|" + word + "|");
}

写 Outputstream

image-20220709003423838

内存的写速度 远远快于 硬盘的写速度,所以,为了平衡这个速度之差,一般通过‘’缓冲区 buffer(内存中的一块区域) ”来处理。

刷盘( flush )

将buffer中数据统一写入文件

  1. 缓冲区满了或者达到一定阈值.
  2. 时间过了一段时间了.
  3. 进程主动刷盘

注意:这三种情况可能导致想要写入的和最终写入的不一致

image-20220709004504867

因此,通常在关闭之前,统一做一次手动刷盘

利用 OutputStreamWriter 进行字符写入

OutputStream 和 FileOutputStream写入

OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中,所以使用 FileOutputStream

OutputStream -> FileOutputStream:

  • 如果文件之前不存在,则会进行创建,创建可能失败,常见失败原因:

    • 权限
    • 路径上的目录还不存在
  • 如果文件之前存在,会清空之前的文件内容,重新写入,所以很少用 file.createNewFile()

OutputStream os1 = new FileOutputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\Lay.txt");

方法

都是按字节写入

image-20220709005232515

       // 一次写一个
        try (OutputStream os = new FileOutputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\world.txt")) {

            // 0xe6 0x88 0x91 是 "我" 的 UTF-8 编码的字节序列
//            os.write(0xe6);
//            os.write(0x88);
//            os.write(0x91);

            os.write(0x20);     // 空格
            os.write(0x0d);     // '\r'
            os.write(0x0a);     // '\n'

            os.write(0x65);
            os.write(0x65);
            os.write(0x65);

            os.flush();     // 确保把缓冲区内可能遗留的数据全部写入 Output 设备中
        }

        // 一次写一堆
        try (OutputStream os = new FileOutputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\world.txt")) {
            byte[] buf = { 0x65, 0x65, 0x20, 0x65, 0x0d, 0x0a, (byte)0xe6, (byte)0x88, (byte)0x91 }; // 0xe6超过byte,强转
            
            String s = "Nothing"; 
            byte[] buf1 = s.getBytes();
            String s1 = "你好中国"; 
            byte[] buf2 = s.getBytes("utf-8");
            
            os.write(buf, 2, 4);  // 从第二个开始,写4个
         	os.write(buf1);  // 整个 buf 的数据全部写入
			os.write(buf2);
            
            os.flush();
        }
Write 和 OutputStreamwriter 写入

Writer抽象类 --> OutputStreamwriter实现类,按字符串写入

        // 利用Writer.write: 以字符串形式写入
        try (OutputStream os = new FileOutputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\world.txt")) {
            try (Writer writer = new OutputStreamWriter(os, "UTF-8")) {
                writer.write("你好中国\r\n");
                writer.write("没什么");
                writer.flush();
            }
        }
利用 PrintWriter 找到我们熟悉的方法

上述,我们其实已经完成输出工作,但总是有所不方便,我们接来下将 OutputStream 处理下,使用

PrintWriter 类来完成输出,因为PrintWriter 类中提供了我们熟悉的 print/println/printf 方法

1. Writer抽象类 --> OutputStreamwriter实现类 
2. Printwriter类,调用方法: 
	println(...) 
    print(...) 
    printf (fmt,...)
try (OutputStream os = new FileOutputStream("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\world.txt")) {
    try (Writer writer = new OutputStreamWriter(os, "UTF-8")) {
        try (PrintWriter printWriter = new PrintWriter(writer)) {
            printWriter.println(1 + 1);
            printWriter.print(3);
            printWriter.printf("%s + %d", "我", 3);

            printWriter.flush();
        }
    }
}
``


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为你介绍一下如何使用 Java 编写程序实现文件拷贝。 Java 提供了非常方便的 I/O 类来处理文件读写操作。文件拷贝的基本思路是读取源文件中的数据,然后将数据写入到目标文件中。下面是一个简单的文件拷贝程序示例: ```java import java.io.*; public class FileCopy { public static void main(String[] args) throws IOException { // 源文件路径 String srcFilePath = "C:/temp/source.txt"; // 目标文件路径 String destFilePath = "C:/temp/dest.txt"; // 创建输入流和输出流 FileInputStream fis = new FileInputStream(srcFilePath); FileOutputStream fos = new FileOutputStream(destFilePath); // 定义缓冲区,每次读取 1024 个字节 byte[] buffer = new byte[1024]; int len; // 循环读取源文件中的数据,并将数据写入到目标文件中 while ((len = fis.read(buffer)) != -1) { fos.write(buffer, 0, len); } // 关闭输入流和输出流 fis.close(); fos.close(); System.out.println("文件拷贝完成!"); } } ``` 在上面的示例中,我们首先定义了源文件路径和目标文件路径。然后创建了输入流和输出流,用于读取源文件和向目标文件写入数据。我们定义了一个缓冲区,每次从输入流中读取 1024 个字节,并将这些字节写入到输出流中。最后,我们关闭输入流和输出流,并输出一条完成信息。 需要注意的是,上面的代码中使用了 try-catch 语句来捕获可能出现的 IOException 异常。这是因为在文件读写过程中可能会出现异常,比如文件不存在、文件无法读取等等。为了保证程序的健壮性,我们需要使用 try-catch 语句来处理这些异常。 希望这个简单的示例可以帮助你了解如何使用 Java 编写文件拷贝程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值