0

0

Java 文件读取与数据解析:从文本文件构建学生对象教程

心靈之曲

心靈之曲

发布时间:2025-10-15 10:28:02

|

793人浏览过

|

来源于php中文网

原创

Java 文件读取与数据解析:从文本文件构建学生对象教程

本教程详细讲解如何在 java 中读取文本文件,并解析其中的结构化数据(如学生姓名和成绩)以创建对应的 java 对象。文章对比了两种核心方法:使用 `scanner` 自定义分隔符和结合 `scanner` 逐行读取与 `string.split()`,并提供了详细的代码示例、注意事项以及如何将解析数据集成到自定义 `student` 类中。

在 Java 应用程序开发中,从外部文本文件读取并解析结构化数据是一个常见的需求。例如,我们可能需要从一个包含学生姓名和多门成绩的文本文件中提取信息,然后将其转化为 Java 对象,以便后续进行排序、分析或存储。本文将深入探讨两种主要的 Java 文件读取和数据解析策略,并指导您如何将这些数据封装到自定义的 Student 类中。

文本文件格式示例

假设我们有一个名为 main.txt 的文本文件,其内容包含学生姓名和四门课程的成绩,数据之间以逗号和空格分隔,每行代表一个学生的信息:

John Doe, 30, 25, 70, 10
Jane Doe, 33, 20, 80, 15
Christian Pulisic, 70, 60, 50, 20

我们的目标是读取这些数据,将姓名作为字符串,成绩作为整数,并最终创建 Student 对象。

方法一:使用 Scanner 自定义分隔符

java.util.Scanner 类提供了一种方便的方式来解析基本类型和字符串。通过设置自定义分隔符,我们可以让 Scanner 自动按照特定模式分割输入流中的数据。

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

核心思想

此方法的核心在于 scan.useDelimiter()。我们需要定义一个正则表达式,它能匹配数据字段之间的分隔符以及行尾的换行符。

  1. 正确的分隔符: 观察示例文件,数据字段之间是 ", "(逗号后跟一个空格),而不是单纯的 ,。
  2. 处理行尾: \R 是一个特殊的正则表达式,用于匹配任何类型的行结束符(\n, \r, \r\n)。
  3. 组合分隔符: 使用 | (或) 运算符将多个分隔符组合起来,即 \\R|,。

代码示例

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;

// 假设 Student 类已定义
// class Student {
//     String name;
//     List grades;
//     public Student(String name, List grades) {
//         this.name = name;
//         this.grades = grades;
//     }
//     @Override
//     public String toString() {
//         return "Name: " + name + ", Grades: " + grades;
//     }
// }

public class StudentDataReaderDelimiter {

    public static void main(String[] args) {
        // 使用 try-with-resources 确保 Scanner 资源被正确关闭
        try (Scanner scan = new Scanner(new File("main.txt"))) {
            // 设置分隔符为换行符或 ", "
            scan.useDelimiter("\\R|, ");

            List students = new ArrayList<>();

            while (scan.hasNext()) {
                try {
                    String name = scan.next();
                    List grades = new ArrayList<>();
                    for (int i = 0; i < 4; i++) { // 假设有4门成绩
                        if (scan.hasNextInt()) {
                            grades.add(scan.nextInt());
                        } else {
                            System.err.println("Error: Expected an integer grade but found non-integer for student " + name);
                            // 跳过当前非整数 token,或者选择更严格的错误处理
                            scan.next(); // 尝试跳过错误的 token
                            break; // 停止读取当前学生的成绩
                        }
                    }
                    students.add(new Student(name, grades));
                } catch (Exception e) {
                    System.err.println("Error processing data: " + e.getMessage());
                    // 可以在这里添加逻辑来跳过当前行或记录错误
                    // 注意:当使用 useDelimiter("\\R|, ") 时,很难精确地跳过“当前行”
                }
            }

            // 打印解析出的学生信息
            for (Student student : students) {
                System.out.println(student);
            }

        } catch (FileNotFoundException e) {
            System.err.println("Error: File not found at specified path. " + e.getMessage());
        }
    }
}

优缺点分析

  • 优点:
    • 代码简洁,一旦分隔符设置正确,Scanner 会自动处理字段的提取。
    • 对于数据格式相对一致,且字段数量固定的文件,此方法非常高效。
    • 在某些情况下,即使数据分布在多行,只要分隔符匹配,也能正确解析。
  • 缺点:
    • 数据验证不严格: 很难在读取过程中验证每个学生是否恰好有固定数量的字段。如果某行数据不完整或多余,可能会导致解析错误或将下一个学生的数据误认为当前学生的字段。
    • 错误处理复杂: 当出现格式错误时,很难精确地定位到错误的行并跳过,因为 Scanner 已经将整个文件看作一个连续的 token 流。

方法二:逐行读取与 String.split() 结合

这种方法通常被认为是处理结构化文本文件更健壮和可控的方式。它将文件读取和行内解析分为两个独立步骤。

MOKI
MOKI

MOKI是美图推出的一款AI短片创作工具,旨在通过AI技术自动生成分镜图并转为视频素材。

下载

核心思想

  1. 逐行读取: 使用 Scanner 或 BufferedReader 逐行读取文件内容。
  2. 行内解析: 对每一行读取到的字符串,使用 String.split() 方法根据指定的分隔符将其分割成一个字符串数组(tokens)。
  3. 类型转换与验证: 遍历字符串数组,将字符串类型的成绩转换为整数,并进行必要的格式验证。

代码示例

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

// 假设 Student 类已定义
// class Student {
//     String name;
//     List grades;
//     public Student(String name, List grades) {
//         this.name = name;
//         this.grades = grades;
//     }
//     @Override
//     public String toString() {
//         return "Name: " + name + ", Grades: " + grades;
//     }
// }

public class StudentDataReaderSplit {

    public static void main(String[] args) {
        // 使用 try-with-resources 确保 Scanner 资源被正确关闭
        try (Scanner scan = new Scanner(new File("main.txt"))) {
            List students = new ArrayList<>();
            int lineNumber = 0;

            while (scan.hasNextLine()) {
                lineNumber++;
                String line = scan.nextLine(); // 读取整行
                String[] tokens = line.split(", "); // 使用 ", " 分割行

                // 验证 token 数量
                if (tokens.length == 5) { // 1个姓名 + 4个成绩 = 5个 token
                    String name = tokens[0];
                    List grades = new ArrayList<>();
                    boolean allGradesValid = true;

                    for (int i = 1; i < tokens.length; i++) {
                        try {
                            grades.add(Integer.parseInt(tokens[i]));
                        } catch (NumberFormatException e) {
                            System.err.println("Error on line " + lineNumber + ": Invalid grade format for '" + tokens[i] + "'");
                            allGradesValid = false;
                            break; // 跳出当前学生成绩的解析
                        }
                    }

                    if (allGradesValid) {
                        students.add(new Student(name, grades));
                    }
                } else {
                    System.err.println("Error on line " + lineNumber + ": Incorrect number of data fields. Expected 5, found " + tokens.length + ". Line: '" + line + "'");
                }
            }

            // 打印解析出的学生信息
            for (Student student : students) {
                System.out.println(student);
            }

        } catch (FileNotFoundException e) {
            System.err.println("Error: File not found at specified path. " + e.getMessage());
        }
    }
}

优缺点分析

  • 优点:
    • 强大的数据验证: 可以轻松地在每行级别进行字段数量、数据类型等验证,从而更准确地识别和处理错误。
    • 清晰的错误报告: 当数据格式不正确时,可以报告具体的行号和错误信息。
    • 更好的控制: 对每一行数据拥有完全的控制权,可以执行更复杂的解析逻辑。
  • 缺点:
    • 如果单个逻辑记录(如一个学生的所有信息)跨越多行,此方法需要额外的逻辑来拼接这些行。

构建 Student 类并集成数据

为了更好地组织和管理解析出的数据,我们应该定义一个 Student 类来封装学生姓名和成绩。

Student 类定义

import java.util.List;
import java.util.ArrayList;

public class Student implements Comparable {
    private String name;
    private List grades;

    public Student(String name, List grades) {
        this.name = name;
        this.grades = new ArrayList<>(grades); // 深度拷贝,防止外部修改
    }

    public String getName() {
        return name;
    }

    public List getGrades() {
        return new ArrayList<>(grades); // 返回拷贝,防止外部修改
    }

    // 计算平均分(可选)
    public double getAverageGrade() {
        if (grades.isEmpty()) {
            return 0.0;
        }
        int sum = 0;
        for (int grade : grades) {
            sum += grade;
        }
        return (double) sum / grades.size();
    }

    @Override
    public String toString() {
        return "Student [Name=" + name + ", Grades=" + grades + ", Average=" + String.format("%.2f", getAverageGrade()) + "]";
    }

    // 实现 Comparable 接口,用于按姓名字母顺序排序
    @Override
    public int compareTo(Student other) {
        return this.name.compareTo(other.name);
    }
}

在上述的两个数据读取示例中,我们已经将解析出的姓名和成绩列表传递给了 Student 类的构造函数,并创建了 Student 对象。例如,在 StudentDataReaderSplit 类的 main 方法中:

// ... (之前的代码) ...
if (allGradesValid) {
    students.add(new Student(name, grades)); // 创建 Student 对象并添加到列表中
}
// ... (之后的代码) ...

将这些 Student 对象存储在一个 List 中,可以方便地进行后续操作,例如排序:

// 在解析完所有学生数据后
import java.util.Collections; // 导入 Collections 类

// ... (在 main 方法的最后) ...
Collections.sort(students); // 按姓名字母顺序排序学生列表

System.out.println("\n--- Sorted Students ---");
for (Student student : students) {
    System.out.println(student);
}

注意事项与最佳实践

  1. 资源管理: 始终确保文件读取器(如 Scanner)在使用完毕后被关闭,以释放系统资源。Java 7 引入的 try-with-resources 语句是处理此问题的最佳方式,它能自动关闭实现了 AutoCloseable 接口的资源。
    try (Scanner scan = new Scanner(new File("main.txt"))) {
        // 文件读取和解析逻辑
    } catch (FileNotFoundException e) {
        // 异常处理
    }
  2. 异常处理:
    • FileNotFoundException: 当指定的文件路径不存在时抛出。务必捕获并处理此异常,告知用户文件缺失。
    • NumberFormatException: 当尝试将非数字字符串转换为整数时抛出(例如,成绩字段包含字母)。在解析成绩时,应捕获此异常,并妥善处理(例如,跳过该条记录,或将成绩设为默认值)。
  3. 数据校验:
    • 在解析前,检查每行数据是否包含预期的字段数量(如方法二中的 tokens.length == 5)。
    • 对数值类型的数据,除了 NumberFormatException,还可以添加业务逻辑上的校验(例如,成绩是否在 0-100 之间)。
  4. 字符编码: 默认情况下,Scanner 使用平台的默认字符集。如果您的文本文件使用了不同的编码(例如 UTF-8),您需要在创建 Scanner 时明确指定:
    Scanner scan = new Scanner(new File("main.txt"), "UTF-8");
  5. 性能考量:
    • 对于小型到中型文件,Scanner 已经足够高效。
    • 对于非常大的文件(GB 级别),考虑使用 BufferedReader 结合 String.split(),因为 BufferedReader 提供了更高效的缓冲读取机制。

总结

在 Java 中读取文本文件并解析结构化数据,主要有两种高效且常用的方法:使用 Scanner 自定义分隔符和结合 Scanner 逐行读取与 String.split()。

  • Scanner 自定义分隔符 适用于数据格式非常固定且字段数量一致的场景,代码简洁。
  • 逐行读取与 String.split() 结合 提供了更精细的控制和更强大的错误处理能力,尤其适用于需要严格验证每行数据格式的场景,是更推荐的通用方法。

无论选择哪种方法,都应遵循 Java 的最佳实践,包括正确的资源管理、健壮的异常处理和详尽的数据校验,以确保程序的稳定性和数据的准确性。通过将解析出的数据封装到如 Student 这样的自定义类中,可以极大地提高代码的可读性、可维护性和数据操作的便利性。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js正则表达式
js正则表达式

php中文网为大家提供各种js正则表达式语法大全以及各种js正则表达式使用的方法,还有更多js正则表达式的相关文章、相关下载、相关课程,供大家免费下载体验。

512

2023.06.20

正则表达式不包含
正则表达式不包含

正则表达式,又称规则表达式,,是一种文本模式,包括普通字符和特殊字符,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,通常被用来检索、替换那些符合某个模式的文本。php中文网给大家带来了有关正则表达式的相关教程以及文章,希望对大家能有所帮助。

251

2023.07.05

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

745

2023.07.05

java正则表达式匹配字符串
java正则表达式匹配字符串

在Java中,我们可以使用正则表达式来匹配字符串。本专题为大家带来java正则表达式匹配字符串的相关内容,帮助大家解决问题。

214

2023.08.11

正则表达式空格
正则表达式空格

正则表达式空格可以用“s”来表示,它是一个特殊的元字符,用于匹配任意空白字符,包括空格、制表符、换行符等。本专题为大家提供正则表达式相关的文章、下载、课程内容,供大家免费下载体验。

351

2023.08.31

Python爬虫获取数据的方法
Python爬虫获取数据的方法

Python爬虫可以通过请求库发送HTTP请求、解析库解析HTML、正则表达式提取数据,或使用数据抓取框架来获取数据。更多关于Python爬虫相关知识。详情阅读本专题下面的文章。php中文网欢迎大家前来学习。

293

2023.11.13

正则表达式空格如何表示
正则表达式空格如何表示

正则表达式空格可以用“s”来表示,它是一个特殊的元字符,用于匹配任意空白字符,包括空格、制表符、换行符等。想了解更多正则表达式空格怎么表示的内容,可以访问下面的文章。

236

2023.11.17

正则表达式中如何匹配数字
正则表达式中如何匹配数字

正则表达式中可以通过匹配单个数字、匹配多个数字、匹配固定长度的数字、匹配整数和小数、匹配负数和匹配科学计数法表示的数字的方法匹配数字。更多关于正则表达式的相关知识详情请看本专题下面的文章。php中文网欢迎大家前来学习。

532

2023.12.06

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

10

2026.01.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.9万人学习

C# 教程
C# 教程

共94课时 | 7.7万人学习

Java 教程
Java 教程

共578课时 | 52.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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