网络传输

java(文件和流)

2019-01-05  本文已影响0人  luckee

前言

Java中使用IO(输入输出)来读取和写入,读写设备上的数据、硬盘文件、内存、网络......,根据数据的走向可分为输入流和输出流,这个走向是以内存为基准的,即往内存读外面的数据是输入流,从内存中往外写是输出流。
IO总结
字节流和字符流-1
字节流和字符流-2
根据处理的数据类型可分为字节流和字符流。

  1. 字节流可以处理所有数据类型的数据,在java中以Stream结尾。
  2. 字符流处理文本数据,在java中以Reader和Writer结尾。
    在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点。所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列。字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的,但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化 这两个之间通过InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联 在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的 。我们还可以看到:Reader类的read()方法返回类型为int :作为整数读取的字符(占两个字节共16位),范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1;InputStream的read()虽然也返回int,但由于此类是面向字节流的,一个字节占8个位,所以返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。因此对于不能用0-255来表示的值就得用字符流来读取!比如说汉字。

不同平台的符号

符号 Linux Windows
换行符,可以使用System.getProperty("line.separator") \n \r\n
路径分隔符,可以使用File.separator / \
多个路径分隔符,可以使用File.pathSeparator : ;

字节流和字符流的区别

字符流使用了缓存区(内存),执行了write(),文件中不会立刻有内容(除非缓冲区满了或者主动刷新缓冲区),需要等输出流对象关闭了,文件中才会有内容;字节流不使用缓冲区,执行了wirite(),文件中立刻就有内容了。

区别图.png
import java.util.*;
import java.io.*;
public class WriterTest {

    public static void main(String[] args) {
        File file = new File("1.txt");
        Scanner in = null;
        Writer out = null;
        try{
            in = new Scanner(System.in);
            out = new FileWriter(file);//需要定义在try代码块外面,定义在try代码块里面的话,finally中访问不了
            String data = null;
            while(in.hasNext()){
                data = in.nextLine();
                if(data == null){
                    data = "";
                }
                out.write(data);
                //out.flush();刷新缓冲区,将缓冲区的内容输出
            }
            
        }catch(Exception e) {
            e.printStackTrace();
        }
        finally{
            try{
                in.close();
                out.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}
import java.util.*;
import java.io.*;
public class OutputStreamTest {
    public static void main(String[] args) {
        File file = new File("2.txt");
        Scanner in = null;
        FileOutputStream out = null;
        try{
            in = new Scanner(System.in);
            //覆盖式,追加式多一个true参数
            out = new FileOutputStream(file);
            String data = null;
            byte[] bytes = null;
            while(in.hasNext()){
                data = in.nextLine();
                if(data != null){
                    bytes = data.getBytes();
                }else{
                    bytes = "".getBytes();
                }
                out.write(bytes);
            }
        }catch(Exception e) {
            e.printStackTrace();
        }finally{
            try{
                in.close();
                out.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

字节数组和字符串的转换

File

File类用于操作文件或目录本身

变量名 作用
File.pathSeparator 系统相关的多个路径的分隔符,字符串类型,windows为;Unix为:
File.pathSeparatorChar pathSeparator的第一个字符,为字符型
File.separator 系统相关的路径分隔符,字符串类型,Unix类为/,windows为\\(转义)
File.separatorChar separator的第一个字符,为字符型

Path

Java SE 8 API
java.nio.file包中的接口。Path描述的是一种层级结构的路径(目录和文件),一个Path是由多个被分隔符分隔的name element组成(比如c/src/1.txt就是3个),可以和File,URI对象互相转化;Path接口的实现者是不可变的,线程安全的。官方文档描述(节选):

An object that may be used to locate a file in a file system. It will typically represent a system dependent file path. A Path represents a path that is hierarchical and composed of a sequence of directory and file name elements separated by a special separator or delimiter.Paths associated with the default provider are generally interoperable with the java.io.File class. Paths created by other providers are unlikely to be interoperable with the abstract path names represented by java.io.File. The toPath method may be used to obtain a Pathfrom the abstract path name represented by a java.io.File object. The resulting Path can be used to operate on the same file as the java.io.File object. In addition, the toFile method is useful to construct a File from the String representation of a Path. Implementations of this interface are immutable and safe for use by multiple concurrent threads.

Paths

java.nio.file包中的final类,作用是获取Path


Paths.png

另外获取Path的方法是FileSystems.getDefault().getPath(str)和file.toPath()

URI

可以和File,Path转化

Files

Java SE 8 API
Files使用
java.nio.file包中的类,该工具类非常强大,可以操作文件(File的功能全有),读写文件,读取文件属性,读取和设置文件权限,遍历单个目录和整个目录(即递归遍历),复制文件(Path-Path,Path-输出流,输入流-Path);参数是Path(不是File);获取文件大小直接有size方法获取文件的空间使用情况要先获取FileStore对象,再通过FileStore对象来获取

Closeable接口和AutoCloneable接口

前者extends了后者,实现了后者的类都可以使用带资源的try,而且可以抛出任何类型的异常;前者只能抛出IOException

String,StringBuilder,StringBuffer

三者之所以有length方法是因为它们实现了CharSequence接口

DataInput和DataOutput接口

可以将各种类型的数据写入或读取,对于int完整的写入其4个字节,对于double写入其8个字节,其他类型也一样,占用多少字节就写入多少字节,不像OutputStream的write方法(参数为int型的那个)只写入一个字节,如果要写入的数据类型是多个字节的则会截断,只写低位的字节。DataInputStream和DataOutputStream分别实现了这两个接口,RandomAccessFile则同时实现了这两个接口


image.png
image.png

OutputStream

OutputStream是所有字节输出流的祖先,是抽象类

InputStream

InputStream是所有字节输入流的祖先,是抽象类,虽然是阻塞IO,但是InputStream有一个available方法用于返回流当前可以返回的字节数,如果流当前不能读,就返回0,我们可以在读取流之前使用这个方法判断,这样就可以避免造成当前线程的阻塞

  1. 读取单个字节:public int read() throws IOException;
    每次使用read()操作将读取一个字节数据,此时返回的是数据(注意是int类型),如果数据已读完,则int返回-1
  2. 读取内容到字节数组:public int read(byte [] b) throws IOException();
    将内容读取到字节数组中,返回读取的个数,如果读取完毕,则返回-1
  3. 读取内容到部分字节数组:public int read(byte [] b,int off,int len) throws IOException();
    将指定长度的内容读取到字节数组中,返回读取个数,如果读取完毕,则返回-1


    InputStream.png
import java.io.File;
import java.io.FileInputStream;
public class Test {
    public static void main(String[] args) {
        File file = new File("1.txt");
        byte[] bytes = new byte[200];
        int len = 0;
        try(FileInputStream in = new FileInputStream(file)) {
            while((len = in.read(bytes)) != -1) {
                System.out.println(len);
                System.out.println(new String(bytes,0,len,"utf-8"));
            }
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

对于这段代码中的循环,其实执行了两次循环,第一次循环读取完返回文件字节数,并打印出来;第二次循环返回-1,结束循环。

Writer

Writer是所有字符输出流的祖先,是抽象类

Reader

Reader是所有字符输入流的祖先,是抽象类

字节流和字符流的转换

主要使用两个类:OutputStreamWriter、 InputStreamReader
OutputStreamWriter是Writer的子类,所以OutputStreamWriter类对象可以自动转型为Writer类实例,以OutputStream的对象作为构造器参数。
InputStreamReader是Reader的子类,所以InputStreamReader类对象可以自动转型为Reader类实例,以InputStream的对象作为构造器参数。

OutputStreamWriter

直接子类FileWriter

InputStreamReader

直接子类FileReader

PrintWriter、BufferedWriter以及BufferredReader

PrintWriter、BufferedWriter以及BufferredReader
PrintWriter是具有自动行刷新的缓冲字符输出流,这是一个高级流。所谓的自动行刷新,意思就是说:在构造函数中指定autoFlush的值为true时,则 println()、printf() 或 format() 方法将自动刷新输出缓冲区(自动调用flush()方法),但是自动行刷新无疑会增加写出次数而降低写出效率。从PrintWeiter的构造方法中我们就可以知道,该高级流很灵活,接下来我们会介绍BufferedWriter/BufferedReader这一对字符缓冲流,但是,由于BufferedWriter没有PrintWriter使用灵活,所以在实际的操作中,我们往往会使用PrinterWriter/BufferedReader这种组合。

对象和字节数组的转换

对象和字节数组的转换

StringWriter和StringReader

image.png

DataInputStream和DataOutputStream

用于数值型的数据

PushbackInputStream

可预览的输入流,即可以先调用read方法读出来,如果不是自己想要的,就调用unread方法退回去

组合流过滤器

不同的流具有不同的功能,如果想同时具备这些功能,就可以使用组合流过滤器的方式,将一个类型的流传给另一个类型的流的构造器,从而兼具两种流的特性。比如想要兼具有预览功能,使用缓存机制,从文件读取数值类型的流,则可以

DataInputStream din = new DataInputStream(
new PushbackInputStream (
new BufferdInputStream(
new FileInputStream(...))));

IO家族

IO家族.png
IO-InputStream.png IO-OutputStream.png IO-Reader.png IO-Writer.png

常用的流

Buffered input streams read data from a memory area known as a buffer; the native input API is called only when the buffer is empty. Similarly, buffered output streams write data to a buffer, and the native output API is called only when the buffer is full.

即:没有经过Buffered处理的IO, 意味着每一次读和写的请求都会由OS底层直接处理,这会导致非常低效的问题。经过Buffered处理过的输入流将会从一个buffer内存区域读取数据,本地API只会在buffer空了之后才会被调用(可能一次调用会填充很多数据进buffer)。经过Buffered处理过的输出流将会把数据写入到buffer中,本地API只会在buffer满了之后才会被调用。
BufferedReader/BufferedWriter可以将字符流(Reader/Writer)包装成缓冲流,这是最常见用的做法。另外,BufferedReader提供一个readLine()可以方便地读取一行,而FileInputStream和FileReader只能读取一个字节或者一个字符,因此BufferedReader也被称为行读取器

总结上面几种流的应用场景:

RandomAccessFile

RandomAccessFile

RandomAccessFile.png
注意这是个流,不是File。从截图来看,这个流直接继承Object,跟字符流和字节流不是一个系列的。并且实现了DataOutput和DataInput,所以可以读,也可以写。
  1. 是JAVA I/O流体系中功能最丰富的文件内容访问类,它提供了众多方法来访问文件内容。

  2. 由于可以自由访问文件的任意位置,所以如果需要访问文件的部分内容,RandomAccessFile将是更好的选择。

  3. 可以用来访问保存数据记录的文件,文件的记录的大小不必相同,但是其大小和位置必须是可知的。

  4. 这个类在很多资料上翻译成中文都是:随机访问文件,在中文里,随机是具有不确定的含义,指一会访问这里,一会访问那里的意思。如果以这种语义来解释的话,就会感到很困惑。其实,Random在英文中不仅仅有随机,还有任意的意思。如果中文名为任意访问文件是不是就会更好的理解。任意表示我们可以指定文件中任何一个位置去操作一个文件。

Charset字符集类

对于无法表示的字符,全用\ufffd,即显示?
utf-16BE(big-endian)遵从高位在前,utf-16LE(little-endian)遵从低位在前

方法 描述
availableCharsets 静态方法,返回虚拟机可用的字符集的map,键为字符集名称,值为字符集对象
forName 静态方法,根据字符集名称获取字符集对象
name 返回字符集名称
aliases 返回该字符集的别名集合
ByteBuffer encode(String str) 将字符串编码成字节序列
CharBuffer decode(ByteBuffer buffer) 解码给定的字节序列,返回字符序列
方法 描述
byte[] array() 返回该字节序列的字节数组
static ByteBuffer wrap(byte[] bytes) 将字节数组包装成字节缓冲区(字节序列)
方法 描述
char[] array() 返回该字符缓冲区的字符数组
char charAt(int index) 返回指定索引的代码单元

对象序列化

要序列化的类需要实现Serializable接口,该接口没有方法,属于标记接口
ObjectInputStream和ObjectOutputStream,readObject方法和writeObject方法。序列化使用序列号机制,给每个序列化的对象分配一个唯一的序列号,所以在保存一个序列化对象到文件中时,如果之前已经保留存了,后面保存则会引用前面保存的,读取也是一样的道理

NIO

Path

InputStream in = ...;
CRC32 crc = new CRC32();
int c;
while((c = in.read()) != -1) { crc.update(c)}
rerurn crc.getValue();//返回校验和
Path path = Paths.get(str);
FileChannel channel = FileChannel.open(path);
FileLock lock = channel.lock();//or tryLock

正则表达式,\[需要转义

语法 描述
字符
c 字符c
\unn,\xnn,\0nn 以特定进制的形式给定代码单元的字符
\t,\n,\r,\f,\a,\e 控制字符,制表、换行、回车、换页符、警告符、逃逸符
字符类
[C1C2...Cn] 字符集中的任何一个字符,如[0-9]表示任何数字字符
[^C1C2...Cn] ^也可以用字符集的补集,如[^0-9]表示任何非数字字符
[C1C2...&&B1B2...] 两个集合的交集
预定义字符类
\d 一个数字字符,等同于[0-9]
\D 一个非数字字符,等同于[^0-9]
\w 一个词语类字符,即[0-9a-zA-Z_],包括数字,大小写字母,下划线
\W 一个非词语类字符
\s 空白字符
\S 非空白类字符
边界匹配符
^ $ 输入的开头和结尾,或者多行模式下的行的开头和结尾
\A 输入的开头
\z 输入的结尾
量词
X? 0或1个X
X* 0或多个X
X+ 1或多个X
X{n} n个X
X{n,} 至少n个X
X{n,m} n至m个X
集合操作
XY 任何X中的字符串,后面紧跟Y中的字符串
XIY X或Y中的字符串
String patternString = ...;
Pattern pattern = Pattern.compile(patternString);//获取模式对象
String input = ...;
Matcher matcher = pattern.matcher(input)//获取匹配器
if(matcher.matchs())//判断input字符串是否匹配
String patternString = ...;
Pattern pattern = Pattern.compile(patternString);//获取模式对象
String input = ...;
Matcher matcher = pattern.matcher(input)//获取匹配器
//获取输入中匹配到的字符串
while(matcher.find()) {
  int start = match.start();
  int end = matcher.end();
  String matchString = input.subString(start, end);
}
String patternString = ...;
Pattern pattern = Pattern.compile(patternString);//获取模式对象
String input = ...;
Matcher matcher = pattern.matcher(input)//获取匹配器
//替代第一个匹配到的
String str = matcher.replaceFirst(anotherString)
//替代所有匹配到的
String str1 = matcher.replaceAll(anotherString)//

URL

String urlString = "https://www.baidu.com";
URL url = new URL(urlString);
InputStreamReader = new InputStreamReader(url.openStream());
// 方法一 
           URL url = new URL("http://www.sina.com.cn");
           URLConnection urlcon = url.openConnection();
           InputStream is = urlcon.getInputStream();
          
            // 方法二
           URL url = new URL("http://www.yhfund.com.cn");
           HttpURLConnection urlcon = (HttpURLConnection)url.openConnection();
           InputStream is = urlcon.getInputStream();
          
           //方法三
           URL url = new URL("http://www.yhfund.com.cn");
           InputStream is = url.openStream();
long begintime = System.currentTimeMillis();
          
           URL url = new URL("http://www.yhfund.com.cn");
           HttpURLConnection urlcon = (HttpURLConnection)url.openConnection();
           urlcon.connect();         //获取连接
           InputStream is = urlcon.getInputStream();
           BufferedReader buffer = new BufferedReader(new InputStreamReader(is));
           StringBuffer bs = new StringBuffer();
           String l = null;
           while((l=buffer.readLine())!=null){
               bs.append(l).append("/n");
           }
           System.out.println(bs.toString());
          
           //System.out.println(" content-encode:"+urlcon.getContentEncoding());
           //System.out.println(" content-length:"+urlcon.getContentLength());
           //System.out.println(" content-type:"+urlcon.getContentType());
           //System.out.println(" date:"+urlcon.getDate());
                
           System.out.println("总共执行时间为:"+(System.currentTimeMillis()-begintime)+"毫秒");
        }catch(IOException e){
           System.out.println(e);
       }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读