首页 > Java > java教程 > 正文

Java 文件解析与数据处理:解决 ArrayList 空值及排序问题

心靈之曲
发布: 2025-11-10 17:15:24
原创
724人浏览过

Java 文件解析与数据处理:解决 ArrayList 空值及排序问题

在 Java 应用开发中,从外部文件读取结构化数据并进行处理是常见的任务。然而,这一过程常常伴随着各种潜在的问题,尤其是在数据格式不严格时。本文将聚焦于一个典型的场景:从 CSV 格式文件读取电影信息,将其分类存储到 `ArrayList` 中,并按年份排序。我们将分析导致 `ArrayList` 为空和数据解析异常的常见原因,并提供专业的解决方案。

1. 问题分析:数据解析的陷阱

在处理文件输入时,开发者经常会遇到两种主要问题:

  1. NumberFormatException: 当尝试将包含非数字字符的字符串转换为数字类型(如 int 或 double)时,会抛出此异常。这通常是由于输入字符串中包含意外的空格、换行符或其他不可见字符。
  2. 数据分类失败: 即使数据被成功读取,如果分类逻辑(例如,基于字符串比较)不够健壮,也可能导致数据无法正确匹配到预期的类别,从而使相应的 ArrayList 保持为空。

在给定的电影信息处理案例中,原始代码在尝试解析电影年份和流派时遇到了上述问题。例如,当文件中的一行数据为 Schindler's List, 1994, War, R 时:

  • movieInfo[1] 得到的是 " 1994",而非 "1994"。直接使用 Integer.parseInt(" 1994") 会导致 NumberFormatException。
  • movieInfo[2] 得到的是 " War",而非 "War"。因此,genre.equals("War") 的判断始终为 false,导致所有电影都无法被正确分类到任何流派的 ArrayList 中。

这些问题的根源在于文件数据中的额外空格,而默认的 String.split(",") 方法无法自动处理这些空格。

2. 解决方案:数据清洗与健壮解析

为了解决上述问题,我们需要在数据读取和解析阶段进行必要的清洗。有两种主要方法可以实现这一点:

2.1 使用 String.trim() 方法

String.trim() 方法可以移除字符串两端的空白字符(包括空格、制表符、换行符等)。这是最直接且易于理解的方法。在解析每个字段后,立即调用 trim() 方法。

立即学习Java免费学习笔记(深入)”;

// 原始代码:
// String[] movieInfo = movie.split(",");
// String title = movieInfo[0];
// int year = Integer.parseInt(movieInfo[1]);
// String genre = movieInfo[2];

// 改进后:
String[] movieInfo = movie.split(",");
String title = movieInfo[0].trim(); // 移除标题两端空格
int year = Integer.parseInt(movieInfo[1].trim()); // 移除年份两端空格后再转换
String genre = movieInfo[2].trim(); // 移除流派两端空格
String rating = movieInfo[3].trim(); // 移除评分两端空格
登录后复制

通过在每个字段上调用 trim(),可以确保 Integer.parseInt() 接收到纯数字字符串,并且流派字符串与预定义的常量完全匹配。

2.2 使用正则表达式优化 String.split()

更优雅且推荐的方法是利用正则表达式来增强 split() 方法。我们可以让 split() 不仅在逗号处分割,还能自动忽略逗号后的任意数量的空白字符。

小文AI论文
小文AI论文

轻松解决论文写作难题,AI论文助您一键完成,仅需一杯咖啡时间,即可轻松问鼎学术高峰!

小文AI论文 69
查看详情 小文AI论文
// 改进后:
// 使用正则表达式 ",\s*"。
// "," 表示匹配一个逗号。
// "\s*" 表示匹配零个或多个空白字符(空格、制表符、换行符等)。
String[] movieInfo = movie.split(",\s*");
String title = movieInfo[0]; // 无需再trim,因为split已经处理了
int year = Integer.parseInt(movieInfo[1]); // 无需再trim
String genre = movieInfo[2]; // 无需再trim
String rating = movieInfo[3]; // 无需再trim
登录后复制

这种方法简化了后续代码,因为每个 movieInfo 数组元素在被访问时已经去除了前导空白。

3. 数据排序:实现 Comparator

为了实现按年份对电影进行排序,我们需要定义一个 Comparator。Comparator 是一个函数式接口,用于定义两个对象之间的比较规则。

import java.util.Comparator;

public class MovieComparator implements Comparator<Movie> {
    @Override
    public int compare(Movie m1, Movie m2) {
        // 按照年份升序排序
        return Integer.compare(m1.getYearReleased(), m2.getYearReleased());
    }
}
登录后复制

有了 MovieComparator,就可以使用 ArrayList 的 sort() 方法对电影列表进行排序:

adventure.sort(new MovieComparator());
drama.sort(new MovieComparator());
// ... 对其他所有流派列表进行排序
登录后复制

4. 完整示例代码与改进

以下是整合了数据清洗和排序功能的完整 Java 代码示例。

4.1 MovieListing.txt 文件示例

假设 MovieListing.txt 文件内容如下(请注意逗号后的空格):

Steven Spielberg
John Williams
Schindler's List, 1994, War, R
Amistad, 1997, Drama, R
The Post, 2017, Drama, PG-13
E.T. the Extra-Terrestrial, 1982, Sci Fi, PG
Jurassic Park, 1993, Sci Fi, PG-13
登录后复制

4.2 Director.java

public class Director {
    private String directorName;
    private String composerName;

    public Director(String d, String c) {
        this.directorName = d;
        this.composerName = c;
    }

    public String getDirectorName() {
        return directorName;
    }

    public void setDirectorName(String directorName) {
        this.directorName = directorName;
    }

    public String getComposerName() {
        return composerName;
    }

    public void setComposerName(String composerName) {
        this.composerName = composerName;
    }
}
登录后复制

4.3 Movie.java

public class Movie extends Director { // 注意:Movie继承Director在面向对象设计上可能不合理,但此处保留原结构
    private String title;
    private int yearReleased;
    private String genre;
    private String rating;

    public Movie(String title, int yearReleased, String genre, String rating, String directorName, String composerName) {
        super(directorName, composerName);
        this.title = title;
        this.yearReleased = yearReleased;
        this.genre = genre;
        this.rating = rating;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getYearReleased() {
        return yearReleased;
    }

    public void setYearReleased(int yearReleased) {
        this.yearReleased = yearReleased;
    }

    public String getGenre() {
        return genre;
    }

    public void setGenre(String genre) {
        this.genre = genre;
    }

    public String getRating() {
        return rating;
    }

    public void setRating(String rating) {
        this.rating = rating;
    }

    @Override
    public String toString() { // 重写toString方便调试输出
        return "Movie{" +
               "title='" + title + ''' +
               ", yearReleased=" + yearReleased +
               ", genre='" + genre + ''' +
               ", rating='" + rating + ''' +
               ", director='" + getDirectorName() + ''' +
               ", composer='" + getComposerName() + ''' +
               '}';
    }
}
登录后复制

4.4 MovieComparator.java

import java.util.Comparator;

public class MovieComparator implements Comparator<Movie> {
    @Override
    public int compare(Movie m1, Movie m2) {
        // 按照年份升序排序
        return Integer.compare(m1.getYearReleased(), m2.getYearReleased());
    }
}
登录后复制

4.5 Driver.java (核心逻辑改进)

import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JOptionPane;
import java.io.*;

public class Driver {

    public void start() throws FileNotFoundException {
        // Initialize arraylists
        ArrayList<Movie> adventure = new ArrayList<>();
        ArrayList<Movie> drama = new ArrayList<>();
        ArrayList<Movie> fantasy = new ArrayList<>();
        ArrayList<Movie> romance = new ArrayList<>();
        ArrayList<Movie> sciFi = new ArrayList<>();
        ArrayList<Movie> thriller = new ArrayList<>();
        ArrayList<Movie> war = new ArrayList<>();

        String directorName = "";
        String composerName = "";

        File myObj = new File("MovieListing.txt");
        try (Scanner myReader = new Scanner(myObj)) { // 使用try-with-resources确保Scanner关闭
            if (myReader.hasNextLine()) {
                directorName = myReader.nextLine().trim(); // 读取并trim导演名
            }
            if (myReader.hasNextLine()) {
                composerName = myReader.nextLine().trim(); // 读取并trim作曲家名
            }

            while (myReader.hasNextLine()) {
                String movieLine = myReader.nextLine();
                // 改进点:使用正则表达式 ",\s*" 分割字符串,自动处理逗号后的空格
                String[] movieInfo = movieLine.split(",\s*");

                if (movieInfo.length < 4) { // 简单的数据完整性检查
                    System.err.println("Skipping malformed line: " + movieLine);
                    continue;
                }

                String title = movieInfo[0];
                int year = Integer.parseInt(movieInfo[1]);
                String genre = movieInfo[2];
                String rating = movieInfo[3];

                Movie movie1 = new Movie(title, year, genre, rating, directorName, composerName);

                // sort movies into arraylists
                if (genre.equals("Adventure")) {
                    adventure.add(movie1);
                } else if (genre.equals("Drama")) {
                    drama.add(movie1);
                } else if (genre.equals("Fantasy")) {
                    fantasy.add(movie1);
                } else if (genre.equals("Romance")) {
                    romance.add(movie1);
                } else if (genre.equals("Sci Fi")) {
                    sciFi.add(movie1);
                } else if (genre.equals("Thriller")) {
                    thriller.add(movie1);
                } else if (genre.equals("War")) {
                    war.add(movie1);
                }
            }
        } catch (FileNotFoundException e) {
            System.err.println("Error: MovieListing.txt not found. " + e.getMessage());
            throw e; // 重新抛出异常,让调用者处理
        } catch (NumberFormatException e) {
            System.err.println("Error parsing number: " + e.getMessage());
            // 可以选择跳过该行或进行其他错误处理
        }

        // 调试输出,检查列表是否已填充
        System.out.println("Adventure Movies: " + adventure);
        System.out.println("War Movies: " + war); // 示例

        // Ask the user which genre they would like to view
        String genreChoice = JOptionPane.showInputDialog("Director: " + directorName + "
" +
                "Composer: " + composerName + "
" +
                "Which genre would you like? 
" +
                "1. Adventure 
" +
                "2. Drama 
" +
                "3. Fantasy 
" +
                "4. Romance 
" +
                "5. SciFi 
" +
                "6. Thriller 
" +
                "7. War 
" +
                "Your choice: ");

        // Sort all movie lists by year released
        MovieComparator movieComparator = new MovieComparator();
        adventure.sort(movieComparator);
        drama.sort(movieComparator);
        fantasy.sort(movieComparator);
        romance.sort(movieComparator);
        sciFi.sort(movieComparator);
        thriller.sort(movieComparator);
        war.sort(movieComparator);

        // Display the output dialog box
        StringBuilder output = new StringBuilder();
        output.append("Director: ").append(directorName).append("
");
        output.append("Composer: ").append(composerName).append("

");
        output.append("Genre: ");

        ArrayList<Movie> selectedGenreList = null;
        String genreName = "";

        switch (genreChoice) {
            case "1":
                selectedGenreList = adventure;
                genreName = "Adventure";
                break;
            case "2":
                selectedGenreList = drama;
                genreName = "Drama";
                break;
            case "3":
                selectedGenreList = fantasy;
                genreName = "Fantasy";
                break;
            case "4":
                selectedGenreList = romance;
                genreName = "Romance";
                break;
            case "5":
                selectedGenreList = sciFi;
                genreName = "Sci Fi";
                break;
            case "6":
                selectedGenreList = thriller;
                genreName = "Thriller";
                break;
            case "7":
                selectedGenreList = war;
                genreName = "War";
                break;
            default:
                JOptionPane.showMessageDialog(null, "Invalid genre choice.");
                return;
        }

        output.append(genreName).append("

");
        output.append("Movie Title	Year Released	Rating
");

        if (selectedGenreList != null) {
            for (Movie movie : selectedGenreList) {
                output.append(String.format("%-20s	%-15d	%s
",
                                             movie.getTitle(),
                                             movie.getYearReleased(),
                                             movie.getRating()));
            }
        }
        JOptionPane.showMessageDialog(null, output.toString());
    }
}
登录后复制

4.6 Main.java

import java.io.FileNotFoundException;

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        Driver driver = new Driver();
        driver.start();
    }
}
登录后复制

5. 注意事项与总结

  1. 数据清洗的重要性: 在从外部源(如文件、网络)读取数据时,始终假定数据可能不完全符合预期格式。对输入数据进行严格的验证和清洗是避免运行时错误的关键。String.trim() 和正则表达式是处理这类问题的强大工具
  2. 异常处理: 在文件 I/O 和数据类型转换时,FileNotFoundException 和 NumberFormatException 是常见的受检异常。使用 try-catch 块进行适当的异常处理至关重要,这可以使程序更加健壮,并提供有用的错误信息。使用 try-with-resources 语句(如 try (Scanner myReader = new Scanner(myObj)))可以确保资源(如 Scanner)在使用完毕后自动关闭,避免资源泄漏。
  3. 面向对象设计: 原始代码中 Movie extends Director 的继承关系在语义上可能不完全符合现实世界模型(电影通常“有”一个导演,而不是“是”一个导演)。在实际项目中,更好的设计可能是让 Movie 类包含一个 Director 类型的成员变量(组合关系),而不是继承关系。然而,本文主要关注数据解析和排序,因此保留了原有结构。
  4. 格式化输出: 使用 String.format() 可以更灵活地控制输出字符串的格式,例如对齐文本、设置宽度等,以生成更美观的报告。
  5. 代码可读性: 适当的注释、有意义的变量名和清晰的代码结构对于维护和理解代码至关重要。

通过上述改进,我们不仅解决了 ArrayList 为空和 NumberFormatException 的问题,还使文件数据处理流程更加健壮和专业。这对于任何需要处理外部数据的 Java 应用来说都是基本且重要的实践。

以上就是Java 文件解析与数据处理:解决 ArrayList 空值及排序问题的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号