【JAVA基础小记】——IO流总结

前言

由于File类只能操作文件对象本身。所以对于文件的读写操作,我们最长使用的是IO流。那么IO流按照传从方向分为:输出流,以及输入流:

**输出流:**将内存中的数据输出到磁盘或是网络中去
**输入流:**将磁盘中的数据输入至内存中

最常用的IO流分类如下,下面是对应的实现类:
字节输入流 字节输出流 字符输入流 字符输出流
InputStream OutputStream Reader Writer —(抽象类)
FileInputStream FileOutStream FileReader FileWriter —(实现类)

字节输入流:InputStream

顾名思义,是完成字节输入的IO流。以下代码演示:

首先new一个file类,参数是目标文件的地址

File file = new File("IO流文件/IO流示例.txt");

然后创建一个字节输入流管道,捕获FileNotFound异常,字节输入流仿佛是打通磁盘和内存的一个管道,每次流入到内存一个字节大小的数据

InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

假如将IO流示例.txt文本中只放入一个字母’a’,执行以下:

int fristCode = inputStream.read();
        System.out.println(fristCode);
        System.out.println((char) fristCode);
        System.out.println(inputStream.read());

控制台输出:

在这里插入图片描述

​ 从而我们看到,执行字节输入流的read方法,我们能到到一个字节码,97(字母a的ASCII码),转换成char后输出字母‘a’,并且输入流中再没有其他字节,所以返回-1。

​ 但是我们知道,在UTF-8编码中:字母等于1字节,而中文等于3字节;Unicode编码中:中英文都是两个字节。

​ 从而我们若使用字节输入流,在utf-8编码中,必然出现中文乱码问题,所以这种按照字节读取数据的方式,并不实用。那么由于字节输入流的read()方法每次只读取一个字节,性能较差,我们应该使用更加高效的方法。

使用byte[ ]

​ 假如IO流把文件数据变成了水流,那么我们使用每次只能够得到一个水滴的方式,必然是低效的。从而应该使用字节数组,这好比一个瓢,这样我们一次性就能够得到多个字节的数据。

​ 首先,创建一个字节数组,我们定义整个水瓢的大小是一次性可以舀取9个字节大小的数据流

byte[] buffer = new byte[9];

在创建文件输入流,参数可以直接写入目标文件的相对或绝对路径

FileInputStream fileInputStream = new FileInputStream("IO流文件/IO流示例.txt");
		//读取到的字节数len
        int len = fileInputStream.read(buffer);
        System.out.println("读取到的字节数:"+ len);
        String sr = new String(buffer);
        System.out.println(sr);

那么现在我们把IO流示例.txt文件中写入"菜虚坤"三个中字,控制台输出:

在这里插入图片描述

​ 首先我们看到了,确实读取到9个字节,那么我们就能知道带有参数的read(byte[ ] bytes)方法,其实返回的是我们所用的水瓢的大小(此时是9个字节大小)。另外,由于编译器默认为utf-8编码,从而每三个字节组成了一个中文汉字,所以一个9字节的byte[ ]数组,刚好能放入三个中字,那么改变字节数组大小呢?

在这里插入图片描述

那么必然出现最后一个汉字乱码的情况。

byte[ ]的基础上,使用循环

代码如下:

FileInputStream fileInputStream = new FileInputStream("IO流文件/IO流示例.txt");
        //使用大小为2的字节数组
        byte[] buffer = new byte[2];
        //定义长度
        int len;
        //定义循环
        while ((len = fileInputStream.read(buffer)) != -1) {
            String rs = new String(buffer,0,len);
            System.out.print(rs);
        }

那么这种方式呢,

在循环条件上,首先带参数的read方法会返回字节数组的大小(本例中为2)给变量len,然后之与-1比较,若len为-1,则说明字节输入流已经将数据读取完毕,退出循环。

在循环内部,String方法有三个参数,首先是字节数组的大小,0代表第一个字节索引,即从头开始,,最后一个参数表示的是该string的长度。

当然,此时还是没有完全避免中文乱码问题。

字节输出流:OutputStream

字节输出流呢,将内存中的数据,以字节的形式输出到磁盘中的文件中。

首先,创建一个字节输出流,第二个参数为布尔类型,代表着下次将数据写入文件是,不覆盖上次的数据

OutputStream outputStream = new FileOutputStream("IO流文件/IO流示例.txt",true);

对应于输入流的read方法,OutputStream中有write方法,可以写入字节数据

outputStream.write(97);

write方法将int自动转化为对应编码中的数据,97对应着小写字母"a", 从而文件中:

在这里插入图片描述

我们也可以使用字节数组,写入文件中

        byte[] buffer = new byte[]{97,98,99,100};
        outputStream.write(buffer);

对应的文件中会被写入:

在这里插入图片描述

或是把字符串转成字节码写入文件

buffer = "唱跳rap篮球".getBytes();
        outputStream.write(buffer);

用完记得关闭这个流

outputStream.close();

字节输入、输出流完成文件的复制

**分析:**创建输入输出流,确定文件的地址,以及要复制的目标地址,文件名,将源文件读入输入流中,然后从输入流中得到文件流,在输出至目标文件。

首先,创建字节输入、输出流,确定相关路径

inputStream = new FileInputStream("C:\\Users\\Pictures\\毒液.png");
outputStream = new FileOutputStream("D:\\IO流目标文件夹\\毒液copy.png");

然后我们不可能一个字节一个字节去复制,太过于愚蠢,所以定义一个1024字节(1kb) 的水瓢

byte[] buffer = new byte[1024];

然后,利用我们之前讲到的循环读取、写入方式

 		  //长度 
            int len;
            while ((len = inputStream.read(buffer)) != -1){
                outputStream.write(buffer,0,len);
            }
            System.out.println("复制完成!");
        }

带有参数的read方法每次回返回1024字节大小长度给len(文件的最后一部分未必是1024字节,可能更小),然后判断是否为-1,即是否读入完毕。然后再循环体内部,将输入流中读取到的字节流,源源不断的写至目标地址下的文件中去。write的参数参考以上String方法。

最后别忘了异常抛出以及关闭输入输出通道,释放资源。

若我们使用try-catch结构,可以将资源放在try参数中,程序运行结束会自动释放资源。

try (
    InputStream inputStream = new FileInputStream("/...");
       ){
            //...
        }catch (Exception e){
            e.printStackTrace();
        }

字符输入流——Reader

字符输入流,就是字符为最小单位将文件流读入内存。

首先,我们创建一个字符输入流

File file = new File("IO流文件/IO流示例.txt");
Reader reader = new FileReader(file);

之后我们在文件中写入一个"菜",然后同样使用无参read方法进行读取

int code = reader.read();
        System.out.println(code);
        System.out.println((char)code);

控制台输出结果

在这里插入图片描述

所以无参read方法同样返回了这个字的编码,然后将编码转换成字符类型输出,得到这个汉字

同样的,我们也可以使用循环,逐个字符的对文件中的数据进行输出

 	    int ch;
        while ((ch = reader.read()) != -1){
            System.out.print((char)ch);
        }

控制台输出

在这里插入图片描述

不过在实际使用中,不会逐字符进行操作,所以我们仍然需要一个舀取IO流的水瓢,,由于字符输入流以字符为最小单位,我们的容器就必须是字符数组,其余操作参考字节流

//每次放入3个字符
 char[] buffer = new char[3];

由于字符输出流Writer与字节输出流十分相似,只是输出的最小单位为字符,所以使用方法不再赘述。

缓冲字节输入流——BufferedInputStream

缓冲流中再内存中加入了缓冲池,内存可以在离自己更近的缓冲池中加载数据,大大提高性能

写法如下,首先我们创建一个基本的字节输入流,它可以被包装成高级缓冲输入流

InputStream fis = new FileInputStream("IO流文件/IO流示例.txt");
BufferedInputStream bis = new BufferedInputStream(fis);

我们同样可以用循环来读取

   byte[] bytes = new byte[1024];
   int len;
while ((len =bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

缓冲字符流——BufferedReader

话不多说,直接开始演示

首先我们将一个字符输入流放入try小括号中,定义一个字符数组,以及长度len,循环输出字符

 try (
     Reader reader = new FileReader("IO流文件/IO流示例.txt");
           )
        	{
            char[] chars = new char[2];
            int len;
            while((len = reader.read(chars))!=-1){
                System.out.println(new String(chars,0,len));
            }

但我们遇到一个问题,当字符数组很小时(此时为2),我们输出的数据是这样的

在这里插入图片描述

所以,我们需要按行输出,而不是按照字符

首先将刚才的字符输入流包装在缓冲流中,定义String类型line,然后调用的是缓冲输入流的readline方法

BufferedReader bufferedReader = new BufferedReader(reader);
String line;
while((line = bufferedReader.readLine()) != null){
        System.out.println(line);
      }

这样我们可以得到完整的数据

在这里插入图片描述

缓冲字符输出流——Writer

原理与上相似,是性能更好的输出流

首先我们定义一个字符输出流,将其是否不覆盖上次写入数据设置为true,将其包装在缓冲输出流中

Writer wr = new FileWriter("IO流文件/IO流示例.txt",true);
BufferedWriter bufferedWriter = new BufferedWriter(wr);

然后我们将数据写入,newline方法起到换行作用。

写完数据,我们务必要关闭流,因为关闭的同时,执行一个刷新操作,帮助我们把数据写入文件,如果使用后不关闭,不仅浪费资源,且可能导致部分数据写入时丢失

        bufferedWriter.newLine();
        bufferedWriter.write("这句话是使用缓冲字符输出流写的");
        bufferedWriter.newLine();
        bufferedWriter.write("这一句也是");
        bufferedWriter.close();

写入后

在这里插入图片描述

字节输入转换流——InputStreamReader

将字节流转换为字符流,首先创建一个字节输入流,然后利用字节输入转换流,把这个字节流(默认时utf-8编码)转换成字符输入流,且时GBK编码格式,然后逐行输出

  InputStream is =  new FileInputStream("IO流文件/IO流示例.txt");
  Reader isr = new InputStreamReader(is,"GBK");
  BufferedReader bufferedReader = new BufferedReader(isr);
  String line;
       while ((line =bufferedReader.readLine())!= null)
        {
            System.out.println(line);
        }

输出结果,由于GBK与utf-8编码中,中文所占字节数不同,所以转换完成后必定乱码

在这里插入图片描述

字节输出转换流——OutputStreamWriter

道理同上,直接上例子,这次直接用utf-8格式,不会乱码

首先创建字节输出流,并将不覆盖上一次数据内容设置为true,然后将此字节流转换成字符输出流,编码格式utf-8

用write方法写入一句话

OutputStream outputStream = new FileOutputStream("IO流文件/IO流示例.txt",true);
Writer writer =new  OutputStreamWriter(outputStream,"UTF-8");
writer.write("\r\n这一句话来自字符转换输出流!");

执行后,文件中:

在这里插入图片描述

序列化与反序列化

简单的说:

序列化即为把java对象存入文件中取,反序列化就是拿出java对象放入内存中

序列化与反序列化分别对应IO流:

序列化: —对象字节输入流 ObjectInputStream
反序列化: —对象字节输出流 ObjectOutputStream

示例:首先创建一个字节流将java类写入文件,然后将其包装成对象字节输出流

然后创建一个user类,调用writeObject方法,将该java对象存入文件中,关闭流

OutputStream os = new FileOutputStream("IO流文件/IO流示例.txt");
ObjectOutputStream oos = new ObjectOutputStream(os);
user user = new user("cxk","123","菜虚坤");
oos.writeObject(user);
oos.close();
System.out.println("对象序列化成功!");

将java对象写入后,如何进行读取呢?

首先创建输入流,将字节输入流包装成对象输入流,使用readObject方法,读取java对象

InputStream inputStream = new FileInputStream("IO流文件/IO流示例.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
System.out.println(objectInputStream.readObject());
System.out.println("反序列化成功!");

控制台输出结果

在这里插入图片描述

Properties属性集对象

与map相似,Properties可以保存键值对,通常被当作属性文件使用

示例,首先创建一个Properties对象,用setProperty方法放入它的key和value

Properties properties = new Properties();
        properties.setProperty("admin1","123");
        properties.setProperty("admin2","321");

创建一个字节输出流对象,使用properties的store方法,将键值对放入目标文件,第二个参数是用来做一个备注

OutputStream os = new FileOutputStream("D:\\IO流目标文件夹\\user.properties");
properties.store(os,"yes!");
os.close();
System.out.println("成功!");

得到的文件

在这里插入图片描述

根据键的名字取值

properties.load(new FileInputStream("D:\\IO流目标文件夹\\user.properties"));
   //根据键取值
   System.out.println(properties.getProperty("admin1"));

打印流

高效的IO流,因为底层也实现了缓冲机制

用法

//创建打印流
PrintStream ps =new PrintStream(outputStream);
//字符打印流
PrintWriter pw = new PrintWriter(outputStream);
pw.println("这是一条由打印流打出的数据");
pw.close();

这样就将数据打印到了文件夹里

**重定向:**让本该在控制台显示的东西打印到这个文件中

OutputStream outputStream = new FileOutputStream("IO流文件/IO流示例.txt",true);
        PrintStream printStream = new PrintStream(outputStream);
		//setOut改变了打印流的走向
        System.setOut(printStream);

        System.out.println("这一条是来自控制台打印的重定向日志"+System.nanoTime());
        printStream.close();

从而,在控制台中什么也没有,而在目标文件中

在这里插入图片描述

以上是关于IO流基础的一些总结。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
接入第三方登录是让用户方便快捷地使用已有账号登录你的网站或应用程序,提高用户体验的一种方式。本文将介绍如何使用 PHP 实现微信公众号第三方登录。 1. 获取微信授权 首先,需要获取微信用户的授权。具体步骤如下: 1)引导用户打开微信授权页面: ```php $appid = 'your_appid'; $redirect_uri = urlencode('http://yourdomain.com/callback.php'); $scope = 'snsapi_userinfo'; $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=$appid&redirect_uri=$redirect_uri&response_type=code&scope=$scope&state=STATE#wechat_redirect"; header("Location: $url"); ``` 其中,`$appid` 是你的微信公众号的 AppID,`$redirect_uri` 是授权后回调的 URL,`$scope` 是授权作用域,可以是 `snsapi_base` 或 `snsapi_userinfo`,`$state` 是自定义参数,用于防止 CSRF 攻击。 2)获取授权码: 用户同意授权后,会重定向到 `$redirect_uri` 指定的 URL,带上授权码 `code` 和 `state` 参数。 ```php $code = $_GET['code']; $state = $_GET['state']; ``` 3)获取 access_token 和 openid: 使用授权码 `code` 获取 `access_token` 和 `openid`。 ```php $access_token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=$appid&secret=$secret&code=$code&grant_type=authorization_code"; $response = file_get_contents($access_token_url); $result = json_decode($response, true); $access_token = $result['access_token']; $openid = $result['openid']; ``` 其中,`$secret` 是你的微信公众号的 AppSecret。 2. 获取用户信息 获取到 `access_token` 和 `openid` 后,可以使用以下代码获取用户信息: ```php $userinfo_url = "https://api.weixin.qq.com/sns/userinfo?access_token=$access_token&openid=$openid&lang=zh_CN"; $response = file_get_contents($userinfo_url); $userinfo = json_decode($response, true); ``` 其中,`$userinfo` 包含用户的昵称、头像等信息。 3. 将用户信息保存到数据库 最后,将获取到的用户信息保存到数据库中,以便下次使用时快速登录。 ```php // 连接数据库 $con = mysqli_connect('localhost', 'username', 'password', 'database'); mysqli_set_charset($con, "utf8"); // 查询用户是否已存在 $sql = "SELECT * FROM users WHERE openid='$openid'"; $result = mysqli_query($con, $sql); if (mysqli_num_rows($result) == 0) { // 用户不存在,插入新用户信息 $nickname = mysqli_real_escape_string($con, $userinfo['nickname']); $headimgurl = mysqli_real_escape_string($con, $userinfo['headimgurl']); $sql = "INSERT INTO users (openid, nickname, headimgurl) VALUES ('$openid', '$nickname', '$headimgurl')"; mysqli_query($con, $sql); } // 保存用户登录状态 $_SESSION['openid'] = $openid; ``` 以上就是使用 PHP 实现微信公众号第三方登录的步骤。需要注意的是,为了确保安全性,应该对用户输入的数据进行过滤和验证,防止 SQL 注入和 XSS 攻击等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值