Java I/O系统学习系列三:I/O流的典型使用方式

尽管可以通过不同的方式组合IO流类,但我们可能也就只用到其中的几种组合。下面的例子可以作为典型的IO用法的基本参考。在这些示例中,异常处理都被简化为将异常传递给控制台,但是这只有在小型示例和工具中才适用。在代码中,你需要考虑更加复杂的错误处理方式。

  同样,本文会包括如下几个方面:

  缓冲输入文件

  从内存输入

  格式化的内存输入

  基本的文件输出

  存储和恢复数据

  读写随机访问文件

  实用工具

  总结

 

1. 缓冲输入文件

  如果想要打开一个文件用于字符输入,可以使用以String或File对象作为文件名的FileReader。为了提高速度,我们可以对那个文件进行缓冲,那么我们需要将所产生的引用传给一个BufferedReader构造器。通过使用其readLine()方法来逐行读取文件,当readLine()返回null时,就到了文件末尾。

复制代码

public class BufferedInputFile {

    public static String read(String fileName) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader(fileName));
        StringBuilder str = new StringBuilder();
        String temp = null;
        while((temp = br.readLine()) != null) {
            str.append(temp + "\n");
        }
        br.close();
        return str.toString();
    }
    
    public static void main(String[] args) {
        try {
            System.out.println(BufferedInputFile.read("pom.xml"));
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

复制代码

  文件的全部内容都累积在字符串str中,最后记得调用close()来关闭流。

2. 从内存输入

  在下面的示例中,从上面的BufferedInputFile.read()读入的String结果被用来创建一个StringReader。然后调用read()每次读取一个字符,并把它打印到控制台。

复制代码

public class MemoryInput {
    
    public static void main(String[] args) {
        try {
            StringReader sr = new StringReader(BufferedInputFile.read("pom.xml"));
            int c;
            while((c = sr.read()) != -1) {
                System.out.print((char)c);
            }
        }catch(Exception e) {
            
        }
    }

}

复制代码

  需要注意的是read()是以int形式返回下一个字节,因此必须将类型强转为char才能显示正确结果。

3. 格式化的内存输入

  要读取格式化数据,可以使用DataInputStream,它是一个面向字节的I/O类,我们可以用InputStream以字节的形式读取任何数据。

复制代码

public class FormattedMemoryInput {

    public static void main(String[] args) {
        try {
            DataInputStream di = new DataInputStream(new ByteArrayInputStream(BufferedInputFile.read("pom.xml").getBytes()));
            while(di.available() != 0) {
                System.out.print((char)di.readByte());
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

复制代码

  这里需要注意必须为ByteArrayInputStream的构造函数提供字节数组,而ByteArrayInputStream传递给DataInputStream之后进行了一次“装饰”,可以进行格式化输入(比如直接读取int、double等类型),这里我们只是通过readByte读取单个字节,调用该方法时任何字节的值都是合法的结果,因此返回值是不能用来检测输入是否结束,这里我们使用available()方法查看还有多少可供存取的字符来判断是否结束。

4. 基本的文件输出

  FileWriter对象可以向文件写入数据,通常会用BufferedWriter将其包装起来用以缓冲输出以提高性能。在本例中为了提供格式化机制,将其装饰成PrintWriter:

复制代码

public class BasicFileOutput {
    
    static String file = "BasicFileOutput.out";
    
    public static void main(String[] args) {
        try {
            BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read("pom.xml")));
            PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            String temp;
            int count = 0;
            while((temp = in.readLine()) != null) {
                out.println(count++ + temp);
            }
            in.close();
            out.close();
            System.out.println(BufferedInputFile.read(file));
        }catch(Exception e) {
            e.printStackTrace();
        }        
    }

}

复制代码

  这里在读取BasicFileOutput.out的内容之前,先调用了out的close()将其关闭,一方面是因为流用完之后需要及时关闭以节省资源,另一方面这里用到了缓冲区,如果不为所有的输出文件调用close(),缓冲区的内容可能不会刷新清空,这样可能导致信息不完整。

  另外Java SE5在PrintWriter中添加了一个辅助构造器,可以很方便根据文件名直接构造一个PrintWriter而不用执行一系列的装饰工作:

PrintWriter out = new PrintWriter(file);

5. 存储和恢复数据

  PrintWriter可以对数据进行格式化,以便阅读。但是为了输出可供另一个“流”恢复的数据,我们需要用DataOutputStream写入数据,并用DataInputStream恢复数据。当然,这些流可以是任何形式,在下面的例子中使用的是一个文件。

复制代码

public class StoringAndRecoveringData {
    
    public static void main(String[] args) {
        try {
            DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("data.txt")));
            out.writeDouble(3.14159);
            out.writeUTF("That was pi");
            out.writeDouble(1.41413);
            out.writeUTF("Square root of 2");
            out.close();
            DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("data.txt")));
            System.out.println(in.readDouble());
            System.out.println(in.readUTF());
            System.out.println(in.readDouble());
            System.out.println(in.readUTF());
            in.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }

}

复制代码

  使用DataOutputStream写入数据,Java可以保证我们可以使用DataInputStream准确地读取数据--无论读和写数据的平台多么不同。当我们使用DataOutputStream时,写字符串并且让DataInputStream能够恢复它的唯一可靠的做法就是使用UTF-8编码,在这个例子中是靠writeUTF()和readUTF()来实现的。

  writeDouble()和readDouble()方法能够写入和恢复double类型的数据。对于其他类型的数据,也有类似的方法用于读写。但是为了保证所有的读写方法都能够正常工作,我们必须知道流中数据项所在的确切位置,因为极有可能将保存的double数据作为一个简单的字节序列、char或其他类型读入。

6. 读写随机访问文件

  使用RandomAccessFile,类似于组合使用了DataInputStream和DataOutputStream,可以同时对一个文件执行读写操作,同时可以利用seek()在文件中到处移动,非常方便,关于RandomAccessFile的详细用法,前面有专门写过<<Java I/O系统:File和RandomAccessFile>>。

  但是在使用RandomAccessFile时,你需要知道文件的排版,这样才能正确地操作它,RandomAccessFile拥有读取基本类型和UTF-8字符串的各种具体方法。

复制代码

public class UsingRandomAccessFile{
    static String file = "rtest.dat";
    static void display() throws IOException{
        RandomAccessFile rf = new RandomAccessFile(file,"r");
        for(int i = 0; i < 7; i++){
            System.out.println("Value " + i + ": " + rf.readDouble());
        }
        System.out.println(rf.readUTF());
        rf.close();
    }
    public static void main(String[] args) throws IOException{
        RandomAccessFile rf = new RandomAccessFile(file,"rw");
        for(int i = 0; i < 7; i++){
            rf.writeDouble(i*1.414);
        }
        rf.writeUTF("The end of the file");
        rf.close();
        display();
        rf = new RandomAccessFile(file,"rw");
        rf.seek(5*8);
        rf.writeDouble(47.0001);
        rf.close();
        display();
    }
}

复制代码

  我们通过writeDouble()方法往文件中写入Double类型数据并通过readDouble()方法来读取,这就是我们需要直到排版的原因,如果读取的不是Double类型的数据有可能出现不是我们想要的结果。

7. 实用工具

  到这里我们学习了多种I/O流的典型用法,比如缓冲输入文件、从内存输入、基本的文件输出、存储和恢复数据、随机读写文件,这些都是Java I/O流比较典型的用法。这里我们发现读取文件、修改、在写出是一个很常见的程序化的任务,但是Java I/O类库的设计有一个问题,就是我们需要编写很多代码来实现这些操作,要记住如何打开文件是一件优点困难的事情。因此,下面是收集的一些帮助类,可以很容易为我们完成这些基本任务,记录在这里,方便以后查看。

  这里收集了两个工具:

  • 一个是TextFile,帮助我们读取和写入文件;
  • 另一个是BinaryFile,帮助我们简化二进制文件的读取。

7.1 读取文件

  TextFile类包含的static方法可以像简单字符串那样读写文本文件,并且我们可以创建一个TextFile对象,它用一个ArrayList来保存文件的若干行,好处是在我们操纵文件内容时可以使用ArrayList的所有功能。

复制代码

public class TextFile extends ArrayList<String>{
    // 将文件读取到一行字符串中
    public static String read(String fileName){
        StringBuilder sb = new StringBuilder();
        try{
            BufferedReader in = new BufferedReader(new FileReader(new File(fileName).getAbsoluteFile()));
            try{
                String s;;
                while((s = in.readLine()) != null){
                    sb.append(s).append("\n");
                }
            }finally{
                in.close();
            }
        }catch (IOException e){
            throw new RuntimeException(e);
        }
        return sb.toString();
    }

    // 单次调用将一个字符串写入一个文件
    public static void write(String fileName,String text){
        try{
            PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
            try{
                out.print(text);
            }finally{
                out.close();
            }
        }catch(IOException e){
            throw new RuntimeException(e);
        }
    }

    // 读取文件,并通过正则表达式将其分离,保存在List中
    public TextFile(String fileName,String splitter){
        super(Arrays.asList(read(fileName).split(splitter)));
        // 因为split()方法有时会在返回的数组第一个位置产生一个空字符串
        if(get(0).equals(""))
            remove(0);
    }

    // 常规的分行读取
    public TextFile(String fileName){
        this(fileName,"\n");
    }

    // 将该TextFile中的内容分行写入指定文件中
    public void write(String fileName){
        try{
            PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
            try{
                for(String item : this){
                    out.println(item);
                }
            }finally{
                out.close();
            }
        }catch(IOException e){
            throw new RuntimeException(e);
        }
    }

    // 简单验证一下
    public static void main(String[] args){
        String file = read("TextFile.java");
        write("test.txt",file);
        TextFile text = new TextFile("test.txt");
        text.write("test2.txt");
        TreeSet<String> words = new TreeSet<String>(new TextFile("TextFile.java","\\W+"));
        System.out.println(words.headSet("a"));
    }
}

复制代码

  这里利用静态的read()方法将文件读取到一个字符串中,再用静态的write()方法将其写入到文件中。然后将新写入的文件作为构造参数构造一个TestFile对象,利用其List的特性,将其内容写入文件test2中。这个类的作用是帮我们读取文件,可以通过静态的read方法读取到一个字符串中,也可以通过构造器读取文件到一个TextFile对象中。

7.2 读取二进制文件

复制代码

public class BinaryFile{
    public static byte[] read(File bFile)throws IOException{
        BufferedInputStream bf = new BufferedInputStream(new FileInputStream());
        try{
            byte[] data = new byte[bf.available()];
            br.read(data);
            return data;
        }finally{
            bf.close();
        }
    }

    public static byte[] read(String bFile)throws IOException{
        return read(new File(bFile).getAbsoluteFile());
    }
}

复制代码

8. 总结

  本文没有总结什么新的知识点,只是总结了一些Java I/O的常见用法比如缓冲输入文件、从内存输入、基本的文件输出、存储和恢复数据、随机读写文件等,并且搜集了两个工具类用来帮助我们读写文件读取二进制文件,以提高些代码的效率。

源码链接: https://pan.quark.cn/s/a4b39357ea24 斐讯K2是一款广受用户青睐的无线路由器,其运行表现稳定且具备较高的可操作性,在DIY爱好者群体中拥有极高的声誉。本资料将系统性地阐述斐讯K2的固件刷机方法及其关联的技术要点。固件升级是路由器爱好者改善设备性能、扩展功能的一种普遍手段,经由替换出厂固件,能够达成更加个性化的网络配置、增强安全防护等目标。斐讯K2固件资源库涵盖了多种知名的非官方固件,诸如Tomato Pheonix 不死鸟、高恪、PandoraBox 潘多拉等,这些固件均具备独特的优势,能够适配不同用户的需求。 1. Tomato Pheonix 不死鸟:Tomato是一款立足于Linux的开源固件,以其精巧、高效而备受推崇。不死鸟版本是专门为华硕及斐讯路由器优化的分支,提供了卓越的QoS(服务质量)配置、详尽的图表监控以及便捷的固件升级途径。对于那些需要精准调控带宽和监测网络状态的用户而言,这是一个理想的选项。 2. 高恪:高恪固件是OpenWrt的定制化版本,着重于操作的便捷性和运行的可靠性,特别适合对路由器操作不甚熟悉的用户群体。它提供了一些实用的功能,例如内置的广告屏蔽、快速测速工具等,同时保留了OpenWrt的适应性。 3. PandoraBox 潘多拉:潘多拉盒是另一款基于OpenWrt的固件,它以丰富的插件库和强大的自定义潜力而闻名。用户能够依据个人需求安装各类插件,实现更多功能,如远程接入、DDNS(动态域名解析服务)等。 4. 官方固件的纯净版本与定制版本:官方固件通常更侧重于稳定性,纯净版意味着未预置额外的应用或服务,适合注重稳定性的用户。定制版则可能包含了制造商的特色功能或优...
源码下载地址: https://pan.quark.cn/s/926926948560 AS3.0与XML结合的通用图片滚动功能,是一种基于ActionScript 3.0和XML技术的动态图像展示方案,非常适合初学者进行学习和实践应用。此项目的关键在于借助XML文件作为数据媒介,用来保存图像的相关参数,例如图像的链接地址、展示的次序等,接着在AS3.0环境中对XML进行解析,并动态地载入和展示这些图像,达成图像的滚动或是循环播放的目的。 我们需要明确ActionScript 3.0(AS3.0)是Adobe Flash Professional以及Flex Builder等开发工具中采用的编程语言,用于构建交互式内容以及丰富的互联网应用。相较于先前的版本,AS3.0在性能上有了大幅度的提升,并且引入了更为规范的面向对象编程模式,涵盖了类、接口以及包等概念。 XML(可扩展标记语言)是一种简明且高效的数据传输格式,既便于人类阅读和编写,也易于机器进行解析和生成。在该项目中,XML文件用于存储图像数据,例如图像的URL、延时的时长、动画的样式等,通过这种方式可以将数据与程序代码分离,从而增强代码的可维护性与可扩展程度。 实施这一图片滚动功能,主要涉及到以下AS3.0的核心知识点: 1. **XML解析**:运用`XML`类来载入并解析XML文件,从而获取图像的清单。AS3.0提供了简便的API来操作XML节点,例如`children()`、`attributes()`等,用以获取子节点和属性值。 2. **事件监听**:借助`EventDispatcher`类来监控载入和解析过程中的事件,比如`Event.OPEN`、`Event.PROGRESS`、`Event...
内容概要:本文介绍了软件许可管理的技术实现方式及相关工具资源,重点阐述了加密外壳(EMS)和API加密两种保护机制。加密外壳通过将程序(如.exe、.dll、.apk)封装在加密壳中,实现运行时内存解密,防止静态反编译和代码篡改,同时支持对数据文件、系统参数及部分代码的加密,并依赖硬件锁(HL)或软件锁(SL)进行授权控制。API加密则通过在代码中嵌入安全验证调用,确保授权合法后才执行核心逻辑。文章还说明了锁的类型(HL/SL)、模式(有驱/AdminMode与无驱/UserMode)、升级路径以及虚拟时钟功能,并描述了产品授权程从功能定义到产品创建、授权生成的全过程,支持通过C2V文件或锁ID复制已有授权状态。文中附带多个开源平台链接和技术博客参考资源。; 适合人群:从事软件版权保护、授权系统开发或安全技术研究的研发人员,尤其是具备一定逆向工程、软件安全基础的1-3年经验开发者。; 使用场景及目标:①构建安全的软件授权体系,防止盗版和非法使用;②实现灵活的功能授权管理(如时效、并发、硬件绑定);③选择合适的加密方案(硬件锁/软锁、有驱/无驱)并集成到现有产品中;④学习加密外壳与API验证的实际应用方法; 阅读建议:此资源侧重于软件许可的技术架构与实施细节,建议结合提供的GitHub、Gitee项目链接及CSDN技术文章深入理解实现原理,并通过实际调试加密壳和模拟授权程加强实践能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值