Java 简单Project 1, 命令行程序

本文介绍如何使用Java编写一个命令行程序,该程序能读取目录下所有文件,用正则表达式搜索文本,并将符合条件的文件打包成ZIP。程序通过File类和Stream API实现,包括对文件路径的操作、正则表达式匹配及文件压缩。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 命令行程序

利用main函数可以接受参数 String[] args 这一特点。

public static void main(String[] args)

2. 任务描述

写一个命令行程序,能够

1. 读取一个目录下的所有文件和子目录

2. 在每个文件中寻找一段文本,支持正则查找

3. 把所有符合条件的文件添加到一个ZIP压缩包。

3. 逐步分析

3.1 

首先,我们建立一个FileSearchApp 的java class, 根据任务描述,直觉上应该有储存文件地址的String path, 储存正则表达式的String regex,储存压缩文件名的String zipFileName, 储存符合条件的文件的路径列表 List<File> zippFiles. 注【1】

public class FileSearchApp {
    private String path;
    private String regex;
    private String zipFileName;
    private List<File> zipFiles = new ArrayList<>();

    public static void main(String[] args) {
        
    }

然后说明用法 "USAGE: FileSearch App path [regex] [zipfile]",接受三个参数,分别为path, 正则表达式,和压缩文件名

public static void main(String[] args) {
        FileSearchApp app = new FileSearchApp();

        switch(Math.min(args.length,3)){
            case 0:
                System.out.println("USAGE: FileSearch App path [regex] [zipfile]");
                return;
            case 3:
                app.setZipFileName(args[2]);
            case 2:
                app.setRegex(args[1]);
            case 1:
                app.setPath(args[0]);
        }
        try {
            app.walkDirectory(app.getPath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

这里walkDirectory的定义如下:注【2】

    public void walkDirectory(String path) throws IOException {
        Files.walk(Paths.get(path)).forEach(f-> processFile(f.toFile()));

    }

对一个String path, 使用Paths.get() 转化为Path 对象,然后通过Files.walk() 以Path为文件树的根节点,产生一个Stream,注【3】 对每一个Path f转化为File后, 进行processFile 的操作。


注释

注【1】

Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。

File对象代表磁盘中实际存在的文件和目录。通过以下构造方法创建一个File对象。

通过给定的父抽象路径名和子路径名字符串创建一个新的File实例。

File(File parent, String child);

通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例。

File(String pathname) 

根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

File(String parent, String child) 

通过将给定的 file: URI 转换成一个抽象路径名来创建一个新的 File 实例。

File(URI uri) 

本次用到的方法:

1public String getName()
返回由此抽象路径名表示的文件或目录的名称。
4public String getPath()
将此抽象路径名转换为一个路径名字符串。
6public String getAbsolutePath()
返回抽象路径名的绝对路径名字符串。
10public boolean isDirectory()
测试此抽象路径名表示的文件是否是一个目录。
11public boolean isFile()
测试此抽象路径名表示的文件是否是一个标准文件。

注【2】

Java Paths 

This class consists exclusively of static methods that return a Path by converting a path string or URI.

static Pathget(String first, String... more)

Converts a path string, or a sequence of strings that when joined form a path string, to a Path.

File.walk() 定义如下

static Stream<Path>walk(Path start, FileVisitOption... options)

Return a Stream that is lazily populated with Path by walking the file tree rooted at a given starting file.

 

注【3】

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

3.2

使用Intellij Idea, [Alt]+[Insert], 自动生成private变量的getter和setter method.


    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getRegex() {
        return regex;
    }

    public void setRegex(String regex) {
        this.regex = regex;
        this.pattern = Pattern.compile(regex);
    }

    public String getZipFileName() {
        return zipFileName;
    }

    public void setZipFileName(String zipFileName) {
        this.zipFileName = zipFileName;
    }

3.3 

ProcessFile 定义

如果在文件中找到了包含目标文本,searchFile()返回true,调用addFileToZip(),将file添加到zipFiles.

 

Pattern, 使用Pattern可以避免重复编译正则表达式。

    private Pattern pattern;

Java 正则表达式和 Perl 的是最为相似的。

java.util.regex 包主要包括以下三个类:

  • Pattern 类:

    pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。

  • Matcher 类:

    Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样,Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。

    public void processFile(File file){
        try{
            if(searchFile(file)){
                addFileToZip(file);
            }
        } catch (IOException|UncheckedIOException e) {
            System.out.println("Error processing file: "+ file + " "+ e);
        }
    }

    public boolean searchFile(File file) throws IOException{
                // Files.lines() actually use UTF-8 by defalut
        return Files.lines(file.toPath(), StandardCharsets.UTF_8)
                .anyMatch( t-> searchText(t));
    }

    public void addFileToZip(File file){
        if(getZipFileName()!=null){
            zipFiles.add(file);
        }
    }

    public boolean searchText(String text){
        return (this.getRegex()==null) ? true :
        this.pattern.matcher(text).find();
        //use  this.pattern.matcher(text).matches()  to full match
    }

    public void zipFiles() throws IOException{
        try(ZipOutputStream out =
                new ZipOutputStream(new FileOutputStream(getZipFileName()))){
            File baseDir = new File(getPath());

            for(File file: zipFiles){
                //filename must be a relative path, not an absolute one
                String fileName = getRelativeFilename(file, baseDir);
                // 1. Create a ZipOutputStream
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipEntry.setTime(file.lastModified());
                out.putNextEntry(zipEntry);
                // 2. Add files to the ZipOutputStream
                Files.copy(file.toPath(), out);
                // 3. Close the ZipOutputStream
                out.closeEntry();
            }
        }
    }

    public String getRelativeFilename(File file, File baseDir){
        String fileName = file.getAbsolutePath().substring(
                baseDir.getAbsolutePath().length());

        // the ZipEntry file name must use "/" ,not"\"
        fileName = fileName.replace("\\","/");
        while(fileName.startsWith("/")){
            fileName = fileName.substring(1);
        }
        return fileName;
    }

 

4. 完整代码

package com.company;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class FileSearchApp {
    private String path;
    private String regex;
    private String zipFileName;
    private Pattern pattern;
    private List<File> zipFiles = new ArrayList<>();

    public static void main(String[] args) {
        FileSearchApp app = new FileSearchApp();

        switch(Math.min(args.length,3)){
            case 0:
                System.out.println("USAGE: FileSearch App path [regex] [zipfile]");
                return;
            case 3:
                app.setZipFileName(args[2]);
            case 2:
                app.setRegex(args[1]);
            case 1:
                app.setPath(args[0]);
        }
        try {
            app.walkDirectory(app.getPath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void walkDirectory(String path) throws IOException {
        Files.walk(Paths.get(path)).forEach(f-> processFile(f.toFile()));

    }

    public boolean searchFile(File file) throws IOException{
                // Files.lines() actually use UTF-8 by defalut
        return Files.lines(file.toPath(), StandardCharsets.UTF_8)
                .anyMatch( t-> searchText(t));
    }

    public boolean searchText(String text){
        return (this.getRegex()==null) ? true :
        this.pattern.matcher(text).find();
        //use  this.pattern.matcher(text).matches()  to full match
    }

    public void addFileToZip(File file){
        if(getZipFileName()!=null){
            zipFiles.add(file);
        }
    }

    public void zipFiles() throws IOException{
        try(ZipOutputStream out =
                new ZipOutputStream(new FileOutputStream(getZipFileName()))){
            File baseDir = new File(getPath());

            for(File file: zipFiles){
                //filename must be a relative path, not an absolute one
                String fileName = getRelativeFilename(file, baseDir);
                // 1. Create a ZipOutputStream
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipEntry.setTime(file.lastModified());
                out.putNextEntry(zipEntry);
                // 2. Add files to the ZipOutputStream
                Files.copy(file.toPath(), out);
                // 3. Close the ZipOutputStream
                out.closeEntry();
            }
        }
    }

    public String getRelativeFilename(File file, File baseDir){
        String fileName = file.getAbsolutePath().substring(
                baseDir.getAbsolutePath().length());

        // the ZipEntry file name must use "/" ,not"\"
        fileName = fileName.replace("\\","/");
        while(fileName.startsWith("/")){
            fileName = fileName.substring(1);
        }
        return fileName;
    }

    public void processFile(File file){
        try{
            if(searchFile(file)){
                addFileToZip(file);
            }
        } catch (IOException|UncheckedIOException e) {
            System.out.println("Error processing file: "+ file + " "+ e);
        }
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getRegex() {
        return regex;
    }

    public void setRegex(String regex) {
        this.regex = regex;
        this.pattern = Pattern.compile(regex);
    }

    public String getZipFileName() {
        return zipFileName;
    }

    public void setZipFileName(String zipFileName) {
        this.zipFileName = zipFileName;
    }

    // Java 6 approach
    /*
    public void walkDirectoryJava6(String path) throws  IOException{
        File dir = new File(path);
        File[] files = dir.listFiles();

        for (File file: files
             ) {
            if(file.isDirectory()){
                walkDirectoryJava6(file.getAbsolutePath());
            }else {
                processFile(file);
            }
        }
    }
    */

    // Java 6 Approach
    /*
    public boolean searchFileJava6(File file) throws FileNotFoundException{
        boolean found = false;
        Scanner sc = null;
        sc = new Scanner(file, "UTF-8");
        while(sc.hasNextLine()){
            found = searchText(sc.nextLine());
            if(found) break;
        }
        sc.close();
        return found;
    }
    */
    //
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值