繁体   English   中英

从Java文本文件中的不同行读取/拆分不同部分

[英]Reading/splitting different parts from different lines in a text file in Java

我正在完成这项作业,我应该从这样的文本文件中读取...

Student Name: John
Student ID: 12344/19
College: Science

Credits Attempted: 15
Credits Earned: 15
Grade Points: 41.2
Course Code Course Title            Credit      Grade
COMP1007,   Amazing Applications of AI,  2,      B
COMP2202,   Fund. of Object Oriented Prog.,  3,      C-
MATH2108,   Calculus (2),            3,          C-
MATH3340,   Discrete Math. for Comp. Sci.,   3,          B-
STAT2101,   Introduction to Statistics,      4,          C+

我应该阅读这个文本文件并计算学生的 GPA 并创建一个看起来像这样的输出文件......

输出文本文件

所以基本上我被卡住了,我不知道该怎么做......我知道如何逐行阅读并将一行分成不同的部分,但这在这里似乎不起作用,因为每一行都与其他。 例如,第一行有两部分,“学生姓名”和本例中的姓名本身“约翰”。 但是在第 9 行中,有四个不同的部分,课程代码、课程名称、学分和成绩。

老实说,我不想在作业上作弊,而只是想了解它

帮助 :)

注意我不能使用 Stream 或 Hashmap 或 BufferedReader

好的,这就是你如何做到的......

  1. 您读入所有文件并将每一行存储在List<String>
  2. 对于前 8 行,您以单独的方式处理每一行。 您甚至可以编写一个单独的函数来解析第0-7行的每一行的必要信息
  3. 所有剩余的行都具有相同的结构。 因此,您可以以相同的方式处理它们以解析出然后处理必要的数据。

如果有不清楚的地方,请对此答案发表评论,我会澄清。

文本文件中的每个数据记录总是有一个Start和一个End 最简单的记录显然是包含在文本文件中的单个分隔行中的记录,其中每个文件行实际上都是一条记录,正如您在典型的CSV 格式数据文件中所看到的那样。 更难读取的记录是多行记录,而每个数据记录由几个连续的文本文件行组成,但每个记录仍然有一个开始和一个结束

记录的开头通常很容易区分。 例如,在您在帖子中提供的文件示例中,显然是以Student Name:开头的任何文件行。

记录的结尾可能并不总是那么容易确定,因为许多应用程序不保存不包含数据值的字段,以帮助提高访问速度并减少文件膨胀。 这个想法是“为什么有一个充满空字段的文本文件”,老实说,这是正确的。 我不是一个文本文件中记录的大风扇,因为反正利用数据库将使好得多意义大量数据。 在任何情况下,总会有一个文件一行将指示记录的开始,因此将是有意义的阅读从开始到下一条记录的开始或在文件的最后一条记录的情况下,从开始结束中文件(EOF)。

这是一个示例(阅读代码中的注释):

// System line separator to use in files.
String ls = System.lineSeparator();   
/* Array will hold student data: Student Name, Student ID, College,
   Credits Attempted, Credits Earned, and finally Grade Points.  */
String[] studentData = new String[6]; 
// String Array to hold Course Table Header Names.
String[] coursesHeader = {"COURSE NO", "COURSE TITLE", "CREDITS", "GRADE"};
// List Interface to hold all the course Data line Arrays for each record
java.util.List<String[]> cousesList = new java.util.ArrayList<>();
// Underlines to be used for Console display and file records
// Under courses Header
String underline1 = "-------------------------------------------------------------";
// Under all the courses
String underline2 = "------------------------------------------------------------------------------------";
    
/* Read and Write to files using 'Try With Resources' so to 
   automatically close the reader an writer objects.     */
try (Scanner reader = new Scanner(new java.io.File("StudentData.txt"), "UTF-8"); 
            java.io.Writer writer = new java.io.FileWriter("StudentsGPA.txt")) {
    // For console display only! [Anything (except caught errors) to Console can be deleted]
    System.out.println("The 'StudentsGPA.txt' file will contain:");
    System.out.println("======================================");
    System.out.println();
        
    // Will hold each line read from the reader
    String line = "";
    /* Will hold the name for the next record. This would be the record START
       but only AFTER the first record has been read.          */
    String newName = "";
        
    // Start reading the 'StudentData.txt' file (line by line)...
    while (reader.hasNextLine()) {
        /* If newName is empty then we're on our first record or 
           there is only one record in file.             */
        if (newName.isEmpty()) {
            line = reader.nextLine();   // read in a file line...
        }
        else {
            /* newName contains a name so we must have bumped into
               the START of a new record during processing of the 
               previous record. We aleady now have the first line 
               of this new record (which is the student's name line) 
               currently held in the 'newName' variable so we just 
               make 'line' equal what is in the 'newName' variable
               and carry on processing the data as normal. in essance,
               we simply skipped a read because we've already read it 
               earlier when processing the previous record.    */
            line = newName;
            // Clear this variable in preparation for another record START.
            newName = "";  
        }
        /* Skip comment lines (lines that start with a semicolon (;)
           or a hash mark (#). Also skip any blank lines.            */
        if (line.startsWith(";") || line.startsWith("#") || line.isEmpty()) {
            continue;
        }
        /* Does this file line start with 'Student Name:'? If so then 
           this is a record START, let's process this record. If not 
           then just keep reading the file.            */
        if (line.startsWith("Student Name:")) {
            /* Let's put the student name into the studentData array at 
               index 0. If it is detected that there has been no name
               applied for some reason then we place "N/A" as the name.
               We use a Ternary Operator for this. So, "N/A" will be a 
               Default if there is not name. This will be typical for 
               the other portions of student data.                */
            studentData[0] = line.isEmpty() ? "N/A" : line.split("\\s*:\\s*")[1].trim();
            /* Let's keep reading the file from this point on and retrieve 
               the other bits of student data to fill the studentData[] 
               Array...                    */
            for (int i = 1; i < 6; i++) {
                line = reader.nextLine().trim();
                /* If we encounter a comment line or a blank line then let's
                   just skip past it. We don't want these.               */
                if (line.startsWith(";") || line.startsWith("#") || line.isEmpty()) {
                    i--;
                    continue;
                }
                /* Store the other portions of student data into the 
                   studentData Array using "N/A" as a default should
                   any student data field contain nothing.        */
                studentData[i] = line.isEmpty() ? "N/A" : line.split("\\s*:\\s*")[1].trim();
            }
            // The current Student's Courses...
            /* Clear the List Interface object in preparation for new 
               Courses from this particular record.               */
            cousesList.clear();
            // Read past the courses header line...We don't want it.
            reader.nextLine();
            // Get the courses data (line by line)...
            while (reader.hasNextLine()) {
                line = reader.nextLine().trim();
                /* Again, if we encounter a comment line or a blank line 
                   in this section then let's just skip past it. We don't 
                   want these. */
                if (line.startsWith(";") || line.startsWith("#") || line.isEmpty()) {
                    continue;
                }
                /* At this point, if we have read in a line that starts
                   with 'Student Name:' then we just hit the START of a 
                   NEW Record! This then means that the record we're 
                   currently working on is now finished. Let's store this 
                   file line into the 'newRecord' variable and then break 
                   out of this current record read.                     */
                if (line.startsWith("Student Name:")) {
                    newName = line;
                    break;
                }
                /* Well, we haven't reached the START of a New Record yet
                   so let's keep creating the courses list (line by line). 
                   Break the read in course line into a String[] array.
                   We use the String#split() method for this with a small
                   Regular Expression (regex) to split each line based on
                   comma delimiters no matter how the delimiter spacing 
                   might be (ex: ","  " ,"  " , " or even "      ,   ").  */
                String[] coursesData = line.split("\\s*,\\s*");
                /* Add this above newly created coursesData string array 
                   to the list.   */
                cousesList.add(coursesData);
            }
            /* Write (append) this current record to new file. The String#format()
               method is used here to save the desired data into the 'StudentGPA.txt'
               file in a table style format.            */
            // Student Data...
            writer.append(String.format("%-12s: %-25s", "ID", studentData[1])).append(ls);
            writer.append(String.format("%-12s: %-25s", "Name", studentData[0])).append(ls);
            writer.append(String.format("%-12s: %-25s", "College", studentData[2])).append(ls);
            // Student Courses...
            // The Header line
            writer.append(String.format("%-13s %-30s %-10s %-4s", coursesHeader[0], 
                          coursesHeader[1], coursesHeader[2], coursesHeader[3])).append(ls);
            // Apply an Underline (underline1) under the header.
            writer.append(underline1).append(ls);
            // Write the Courses data in a table style format to make the Header format.
            for (String[] cData : cousesList) {
                writer.append(String.format("%-13s %-33s %-9s %-4s", 
                              cData[0], cData[1], cData[2], cData[3])).append(ls);
            }
            // Apply an Underline (underline2) under the Courses table.
            writer.append(underline2).append(ls);
                                    
            // Display In Console Window (you can delete this if you want)...
            System.out.println(String.format("%-12s: %-25s", "ID", studentData[1]));
            System.out.println(String.format("%-12s: %-25s", "Name", studentData[0]));
            System.out.println(String.format("%-12s: %-25s", "College", studentData[2]));
            System.out.println(String.format("%-13s %-30s %-10s %-4s", coursesHeader[0],
                               coursesHeader[1], coursesHeader[2], coursesHeader[3]));
            System.out.println(underline1);
            for (String[] cData : cousesList) {
                System.out.println(String.format("%-13s %-33s %-9s %-4s", 
                                    cData[0], cData[1], cData[2], cData[3]));
            }
            System.out.println(underline2);
                
            // The LAST line of each record, the Credits...
            // YOU DO THE CALCULATIONS FOR: totalAttemped, semGPA, and cumGPA
            String creditsAttempted = studentData[3];
            String creditsEarned = studentData[4];
            int credAttempted = 0;
            int credEarned = 0;
            int totalAttempted = 0;
            double semGPA = 0.0d;
            double cumGPA = 0.0d; 
            /* Make sure the 'credits attemted' numerical value is in fact 
               a string representaion of an integer value. if it is then
               convert that string numerical value to integer.  */
            if (creditsAttempted.matches("\\d+")) {
                credAttempted = Integer.valueOf(creditsAttempted);
            }
            /* Make sure the 'credits earned' numerical value is in fact 
               a string representaion of an integer value. if it is then
               convert that string numerical value to integer.  */
            if (creditsEarned.matches("\\d+")) {
                credEarned = Integer.valueOf(creditsEarned);
            }
            // Build the last record line (the Credits string) with the acquired data.
            String creditsString = new StringBuilder("CREDITS: TOTAL.ATTEMPTED ")
                    .append(totalAttempted).append("? EARNED ").append(credEarned)
                    .append(" ATTEMPTED ").append(credAttempted).append("  SEM GPA   ")
                    .append(semGPA).append("?   CUM GPA   ").append(cumGPA).append("?")
                    .toString();
            // Display it to the console Window (you can delete this).
            System.out.println(creditsString);
            System.out.println();
                
            // Write the built 'credit string' to file which finishes this record.
            writer.append(creditsString).append(ls);
            writer.append(ls);  // Blank Line in preparation for next record.
            writer.flush();     // Flush the data buffer - write record to disk NOW.
        }
    }
}
// Trap Errors...Do whatever you want with these.
catch (FileNotFoundException ex) {
    System.err.println("File Not Found!\n" + ex.getMessage());
}
catch (IOException ex) {
    System.err.println("IO Error Encountered!\n" + ex.getMessage());
}

是的,它看起来很长,但如果你去掉所有的评论,你会发现它真的不是。 不要害怕尝试代码。 让它做你想做的事。


编辑:(根据评论)

将每条记录的学生信息部分放入 ArrayList 以便您可以按照自己的方式解析它:

for循环位于上面用于收集学生信息的示例代码中的位置,只需将此循环更改为此代码并按照您想要的方式解析数据:

// Place this ArrayList declaration underneath the 'underline2' variable declaration:
java.util.ArrayList<String> studentInfo = new java.util.ArrayList<>();

然后:

if (line.startsWith("Student Name:")) {
    studentInfo.clear();
    studentInfo.add(line);
    /* Let's keep reading the file from this point on and retrieve 
       the other bits of student data to fill the studentData[] 
       Array...                      */
    for (int i = 1; i < 6; i++) {
        line = reader.nextLine().trim();
        /* If we encounter a comment line or a blank line then let's
           just skip past it. We don't want these.          */
        if (line.startsWith(";") || line.startsWith("#") || line.isEmpty()) {
            i--;
            continue;
        }
        studentInfo.add(line);
    }
    
    // .................................................
    // .... The rest of the code for this `if` block ...
    // .................................................
}

您当然需要更改此循环后的代码以正确表示此 ArrayList。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM