当前位置: 首页 > news >正文

File和IO

元数据与文件读写

Java 的 File 表示文件的元数据。元数据是什么?元数据就是描述数据的数据。文件的元数据就是文件名、文件大小、文件路径等信息。

要读写文件内容,需要用到 Java 的 IO 流,而 IO 流需要提供 File 对象。

File 的常用方法

File 的构造方法需要提供文件的路径,支持绝对路径、相对路径以及网络路径,不过这个路径可以不存在。

  • boolean exists():判断传入的路径是否对应一个真实存在的文件
    • 如果不存在则可以新建文件
      • boolean createNewFile(),新建文件也只会创建文件,文件可以没有后缀
      • boolean mkdir(),创建单级目录
      • boolean mkdirs(),创建多级目录
    • 如果文件存在,可以判断是文件夹还是文件,如果文件不存在则这两个方法都返回 false
      • boolean isFile()
      • boolean isDirectory()
  • 获取文件信息
    • String getAbsolutePath(),返回文件的绝对路径
    • String getPath(),返回构造方法中传入的路径
    • String getParent(),返回父级路径字符串
    • File getParentFile(),返回父级路径 File 对象
    • String getName(),返回文件夹名称、文件名(有后缀带后缀)
    • long length(),返回文件的大小(字节),如果是文件夹也有返回值但是不正确
    • long lastModified():最近修改的时刻
  • 文件权限相关
    • boolean canRead()
    • boolean canWrite()
    • boolean isHidden()
  • list,如果 File 对象不存在、File 对象不是文件夹、File 对象需要的权限不足返回 null,否则返回文件夹下的所有文件(如果是空文件夹则返回一个空数组)
    • String[] list()
    • File[] listFiles()
  • 危险操作
    • boolean delete(),删除文件和空目录,不走回收站
    • boolean renameTo(File dest),重命名或者移动文件,原文件不会保留

基础 IO 流

基础 IO 流可以分成字节流、字符流、输入流和输出流。两两组合就得到了四种搭配:

  • 字节输入流:InputStream

    • int read():每次读取一个字节,取值在 [0, 255] 区间,读完返回 -1。

    • int read(byte[] b) : 读取是尽量将数组填满,返回实际读取的字节数,读完返回-1。

    • int read(byte[] b, int off, int len):跟上一个方法一样,只不过加了一个偏移量,返回实际读取的字节数,读完返回-1。

    • void close()

  • 字节输出流:OutputStream

    • void write(int b):每次写一个字节

    • void write(byte[] b):一个字节一个字节写效率太低,每次写一整组的字节数据

    • void write(byte[] b, int off, int len)

    • void flush()

    • void close()

  • 字符输入流:Reader

    • int read()

    • int read(char[] cbuf)

    • int read(char[] cbuf, int off, int len)

    • void close()

  • 字符输出流:Writer

    • void write(int c)

    • void write(char[] cbuf)

    • void write(char[] cbuf, int off, int len)

    • void write(String str)

    • void write(String str, int off, int len)

    • void flush()

    • void close()

InputStream、OutputStream、Reader、Writer 都是抽象类,都实现了 Closeable 接口,输出流额外实现了 Flushable 接口。

字符流拥有一个长度为 8192 的字节数组缓冲区,充当了内存的作用,Java 程序先将数据读入缓冲区之后每次再到缓冲区中读,写数据的时候先将数据写到缓冲区中,缓冲区写满了就刷盘。输出流的 flush 方法可以主动将缓冲区的数据刷盘。

拷贝文件的示例代码如下,InputStream、OutputStream、Reader、Writer 都是抽象类,需要用到实现类 FileInputStream、FileOutputStream、FileReader、FileWriter。

public void test() throws IOException { File source = new File("C:\\Users\\admin\\Downloads\\python.md"); File dest = new File("C:\\Users\\admin\\Documents\\python.md"); FileInputStream fis = new FileInputStream(source); // 输出流的append参数表示是追加写还是覆盖写,默认是false也就是覆盖写 FileOutputStream fos = new FileOutputStream(dest, true); int res; while (true) { res = fis.read(); if (res == -1) break; fos.write(res); } fos.close(); fis.close(); }

不基础 IO 流

1.缓冲流

BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter,需要传入基础 IO 流对象。

字节缓冲流新增了长度为 8192 的字节数组;而字符缓冲流的缓冲区从长度为 8192 的字节数组升级成了字符数据,长度不变。

字符缓冲流新增了两个方法:

  • String readLine():一次读取一行,遇到换行符结束,读完返回 null
  • void newLine():换行

这两个方法可以区分不同操作系统的换行符。

2.序列化流

将 Java 对象转成字节数组,可以在不同设备之间进行传输。

  • 只有字节流,没有字符流
  • Java 对象所属的类必须实现 Serializable 接口,这是一个标记性空接口,里面没有抽象方法
  • 固定 private static final long serialVersionUID 属性
    • private,外部不可见
    • static,版本号是跟类相关的属性,跟对象没关系
    • final,常量不可二次修改
    • long serialVersionUID,按英语的说法是固定搭配

如果某个属性不想被序列化,可以用 transient 瞬态关键字修饰,反序列化后得到的值是 0 或 null。

public void test() throws IOException, ClassNotFoundException { File file = new File("C:\\Users\\admin\\Documents\\1.txt"); String str = "1234"; // 序列化:将Java对象转成字节数据 FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(str); oos.close(); // 反序列化:将字节数据转成Java对象 FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis); String s = (String) ois.readObject(); ois.close(); // 只关序列化流就可以了,像这些需要传入IO流对象的实例对象在调用close方法时会自动将传入的IO流对象一起关闭 System.out.println(s); }

在我们之前学习的 IO 流中,会返回 -1,返回 null 表示读完了。

但是序列化流不会进行提醒,如果写入了 10 个对象,但是进行了 11 次读取操作就会直接报错,非常粗暴。

比较好的做法是将实例对象存入集合中,然后将集合对象序列化。

3.打印流

打印流可以做到原样输出,在方法中传递什么参数,就直接输出到文件中。如果传入的是一个对象,将这个对象的 toString 方法的返回值输出到文件中。

打印流的特点是只支持输出流:PrintStream、PrintWriter。

  • void print()
  • void println()
  • void printf(String format, Object... args),跟 C 语言的 printf 用法差不多

4.转换流

InputStreamReader:字节输入流 -> 字符输入流,传入 InputStream,但是能调用字符流的方法

  • InputStreamReader(InputStream in)

  • InputStreamReader(InputStream in, String charsetName)

OutputStreamWriter:字节输出流 -> 字符输出流,传入 OutputStream,但是能调用字符流的方法

  • OutputStreamWriter(OutputStream in)

  • OutputStreamWriter(OutputStream in, String charsetName)

将 UTF8 编码的文件读取到程序中,用 GBK 编码写入到新文件中。

public static void main(String[] args) throws IOException { InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\Users\\admin\\Downloads\\1.txt"), "UTF8"); OutputStreamWriter isw = new OutputStreamWriter(new FileOutputStream("C:\\Users\\admin\\Downloads\\2.txt"), "GBK"); char[] arr = new char[256]; int len = 0; while ((len = isr.read(arr)) != -1) { System.out.println(new String(arr, 0, len)); isw.write(arr, 0, len); } isw.close(); isr.close(); }

Unicode 码点

ASCII 码:英文标点符号,一个字节就可以表示。

GBK:1 英2 中,兼容 ASCII 码,英文用一个字节表示,中文用两个字节表示。

Unicode:Unicode 是一个字符集,它为世界上所有字符分配了唯一的编号,这个编号就叫码点。但是,Unicode 并未规定这些字符如何保存和传输,需要具体的编码方式来实现。

UTF-8:Unicode 的一种变长编码方式,可以用 1 到 4 个字节来保存字符,1 英 3 中。

这个时候我想到了 Java 里面的 char 类型,char 可以用来表示中文,但是 char 类型只占用两个字节,这是怎么做到的呢?

其实 char 类型保存的是字符的码点这个唯一编号,并不是保存字符实际的编码,如果一个字符的码点无法用 16bit 表示,就无法用 char 类型存储。

平时我们可以用 char 来表示中文,是因为这些字符的码点可以用两个字节表示,但是有些字符不能,如 emoji 表情。

𠮷 是一个生僻字,就无法用一个 char 表示,需要用 String 表示。

因为 𠮷 要用两个 char 表示,所以 String 的 length 方法返回的是 2,也就是 String 中 char 的个数。

如果想真正统计字符串中有多少个字符,需要用 String 提供的 codePointCount 方法。

http://www.jsqmd.com/news/1098851/

相关文章:

  • 与你的 Elasticsearch 数据对话:使用 Google ADK 和 MCP 构建一个实时语音 agent ,分为 3 个组件
  • 5分钟快速上手:RedisDesktopManager-Windows终极可视化数据库管理工具完整指南
  • 告别串口乱码!STM32F401RCT6用Arduino框架点灯+串口打印保姆级教程
  • C#工业视觉实战:集成工业相机与YOLOv8实现缺陷检测系统
  • 探索兴趣爱好的内涵
  • 廖雪峰Python2教程PDF!20行代码秒杀C语言1000行,速度慢?谁在乎
  • 别再让激光器‘发烧’了!手把手教你用运放搭建高精度恒流源(附LTspice仿真文件)
  • 如何生成字母或数字的*序列*?
  • Dify平台大模型接入实战:从云端API到本地部署全流程指南
  • Postman便携版终极指南:Windows用户的免安装API开发解决方案
  • 别再只会用三极管了!用JFET搭个恒流源给LED调光,实测效果稳如老狗
  • 电脑弹窗拦截工具绿色免费超好用
  • 48.可直接落地!IEC61131-3 ST 完整源码|PLC 物料分拣 + PID 调速 + Modbus 通信
  • 零基础入门MySQL数据分析:从SQL语法到电商实战项目
  • SH9递归对抗驱动的活系统:九层架构理论体系深度研究报告(世毫九实验室原创研究)
  • linux中TCP通信
  • Python之rickshaw包语法、参数和实际应用案例
  • 基于PANDAS的QAbstractTableModel实现高级TableView详细解析(八、在TableView实现冻结窗口)
  • Confluence高危漏洞CVE-2022-26134应急响应与安全加固实战指南
  • 把 Enterprise Services Repository 配成一座稳定的集成设计中枢
  • 洛谷 P2024:[NOI2001] 食物链 ← 扩展域并查集
  • SpringBoot 底层原理完整教程(上篇・配置文件与配置优先级)
  • Anthropic揭秘Claude隔离内幕、加州政府半价用上AI、主权AI来了:今天6件大事
  • 35款自动脱壳工具合集:逆向工程中的“开罐器”与“手术刀”
  • 01 静态分析(Static Analysis)
  • *如何使用* *bc* 进行高级*数学*计算?
  • 只会写业务 UI 走不远!吃透这套 Framework 体系,跳槽大厂拉开薪资差距
  • 打卡信奥刷题(3414)用C++实现信奥题 P10139 [USACO24JAN] Nap Sort G
  • StarRocks StreamLoad 持续写入导致 be 内存增长
  • Kimi LeetCode 3410. 删除所有值为某个元素后的最大子数组和 Python3实现