AP Computer Science A • Data Collections

Using Text Files

Read, parse, and persist structured data beyond a program's runtime using Java file I/O.

AP Computer Science A illustration with code, binary digits, and programming braces
Inside this lesson 10 sections 5 questions 10 figures

SECTION 1

Historical Context & Motivation

From the earliest days of computing, programs needed a way to preserve information after execution ended. Before the era of databases and cloud storage, the humble text file served as the primary medium for persisting data—configuration settings, scientific measurements, logs, and user records all lived as sequences of characters on disk. Understanding file I/O is not merely a historical curiosity; it remains a foundational skill because text files are human-readable, portable across operating systems, and require no special software to inspect. Even modern data-processing pipelines frequently begin and end with plain text formats like CSV, JSON, and TSV, making the ability to read from and write to files a prerequisite for virtually every domain of software engineering.

1956 • IBM 305 RAMAC

The first commercial hard disk drive introduced random-access storage, enabling programs to read and write persistent data files rather than relying exclusively on sequential tape.

1969 • Unix & the File Abstraction

Unix established the paradigm that 'everything is a file,' unifying access to devices, pipes, and data under a single I/O interface that influenced every major language, including Java.

1995 • Java 1.0 — java.io

Java's original I/O library shipped with stream-based classes like FileReader and BufferedReader, giving developers a platform-independent way to handle text files.

2004 • Java 5 — Scanner

The Scanner class simplified tokenized parsing of text input, quickly becoming the preferred tool in educational settings and the AP Computer Science A curriculum.

2014 • Java 8 — Streams & NIO

The introduction of Files.lines() and the Stream API allowed declarative, memory-efficient file processing—though the AP exam still centers on Scanner-based approaches.

The central question that text-file I/O addresses is deceptively simple: how does a program move structured data between volatile memory and persistent storage while keeping that data accessible to both humans and other programs? Answering this question requires understanding file paths, character encoding, buffered reading, parsing tokens, and the crucial practice of resource management—topics we explore in the sections that follow.

SECTION 2

Core Principles & Definitions

Working with text files in Java revolves around a small set of interconnected ideas. Each principle builds on the last: you must locate a file before you can open it, open it before you can read tokens, and close it before your program terminates cleanly. The following grid distills these foundational concepts.

1

File Path & the File Object

A File object (from java.io.File) represents a path on disk. It does not open or read the file; it simply describes where the file is. Use relative paths (e.g., "data/scores.txt") for portability.
2

Scanner for Reading

The Scanner class (from java.util.Scanner) wraps an input source and breaks it into tokens delimited by whitespace or a custom pattern. Methods like nextLine(), nextInt(), and nextDouble() parse the next token into the desired type.
3

hasNext Guards

Before calling a next*() method, you should verify data remains with hasNext(), hasNextLine(), or hasNextInt(). This prevents a NoSuchElementException at runtime.
4

FileNotFoundException

Constructing a Scanner on a File that does not exist throws a checked FileNotFoundException. Java forces you to handle it—either with a try-catch block or by declaring throws FileNotFoundException on the enclosing method.
5

Resource Cleanup

An open file holds an operating-system resource. Always call scanner.close() when finished, or use a try-with-resources statement to guarantee automatic cleanup even if an exception is thrown.
✦

KEY TAKEAWAY

Think of a text file as a book in a library. The File object is the call number that locates the book on the shelf; the Scanner is the reader who opens the book and moves a finger line by line; and closing the Scanner is like returning the book so other patrons—or other parts of your program—can access it.

SECTION 3

Visual Explanation — The File-Reading Pipeline

The diagram below traces the complete lifecycle of reading a text file in Java, from the file system through the Scanner and into your program's data structures. Follow the arrows to see how raw bytes on disk become usable Java objects in memory.

File-Reading Pipeline in JavaDISK: scores.txtAlice 95Bob 87Carol 92Dave 78EOFjava.io.File objectopenSCANNERhasNextLine() → truenextLine()→ "Alice 95"next() → "Alice"nextInt() → 95java.util.ScannerparseMEMORYArrayList<String>[Alice, Bob,Carol, Dave]ArrayList<Integer>[95, 87, 92, 78]Java collectionsLifecycle Summary1Create File2Open Scanner3Read / Parse4Store Data5Close Scanner
The pipeline flows left to right: a File object locates the file on disk; a Scanner reads and tokenizes lines; parsed values are stored in Java collections in memory; and the Scanner is closed to release the resource.

Notice that the Scanner sits at the center of the pipeline. It acts as a translator: on its left side it consumes a stream of characters from disk, and on its right side it emits typed Java values—Strings, ints, doubles—ready for storage in arrays or ArrayList structures. The lifecycle summary at the bottom of the diagram is the pattern you will use in virtually every file-reading method you write for the AP exam: create, open, read, store, close.

SECTION 4

How It Works — Reading and Writing Code Patterns

Java's approach to file I/O is built on the principle of wrapping: you wrap a File object inside a Scanner to read, or inside a PrintWriter to write. This section presents the essential code patterns you will encounter on the AP Computer Science A exam, focusing on the Scanner-based reading pattern, followed by a brief discussion of writing with PrintWriter.

Pattern 1 — Reading Line by Line

The most common file-reading idiom uses a while loop guarded by hasNextLine(). On each iteration the loop calls nextLine() to retrieve one full line of text, which can then be split, parsed, or stored directly. This pattern is robust because it gracefully handles files of any length—including empty files, where the while condition is immediately false.

LINE-BY-LINE READING PATTERN

Scannersc=newScanner(newFile("data.txt"));while(sc.hasNextLine())Stringline=sc.nextLine();//processlinesc.close();Scanner sc = new Scanner(new File("data.txt")); while (sc.hasNextLine()) { String line = sc.nextLine(); // process line } sc.close();

new File("data.txt") creates the path reference. hasNextLine() returns true while unread lines remain. nextLine() consumes and returns the next full line (up to but not including the newline character).

Pattern 2 — Reading Token by Token

When a file's data is whitespace-delimited (spaces, tabs, or newlines), you can use hasNext() with next() to read one token at a time, or hasNextInt() / nextInt() to read typed values. This is convenient when each token is a discrete datum, such as a list of integers or a name-score pair on each line.

TOKEN-BY-TOKEN READING PATTERN

Scannersc=newScanner(newFile("scores.txt"));while(sc.hasNext())Stringname=sc.next();intscore=sc.nextInt();//processname,scoresc.close();Scanner sc = new Scanner(new File("scores.txt")); while (sc.hasNext()) { String name = sc.next(); int score = sc.nextInt(); // process name, score } sc.close();

next() reads the next whitespace-delimited String token. nextInt() reads the next token and parses it as an int. If the token is not an integer, an InputMismatchException is thrown.

Pattern 3 — Writing with PrintWriter

While the AP exam focuses primarily on reading, understanding writing solidifies the concept. A PrintWriter wraps a File and provides familiar print(), println(), and printf() methods. Like Scanner, it must be closed to flush and release resources. If the file does not exist, PrintWriter creates it; if it does exist, the file is overwritten unless you specifically append.

WRITING PATTERN

PrintWriterpw=newPrintWriter(newFile("output.txt"));pw.println("Alice95");pw.println("Bob87");pw.close();PrintWriter pw = new PrintWriter(new File("output.txt")); pw.println("Alice 95"); pw.println("Bob 87"); pw.close();

println() writes a line of text followed by a system-dependent newline. Always call close() to ensure all buffered data is actually written to disk.

⚠

Exception Handling Reminder

Both new Scanner(new File(...)) and new PrintWriter(new File(...)) throw a checked FileNotFoundException. On the AP exam, the simplest approach is to add throws FileNotFoundException to your method signature. In production code, a try-catch block or try-with-resources is preferred.

SECTION 5

Parsing Strategies & Data Formats

Reading raw lines from a file is only half the battle. The other half involves parsing those lines into meaningful data. The strategy you choose depends on how the file is formatted. Below is a visual taxonomy of the most common text-file formats you will encounter on the AP exam and in real-world applications, followed by a detailed table.

Text File Parsing Decision TreeRead a line from filecomma-separated?space-separated?fixed-width or other?CSV — line.split(",")"Alice,95,A"→ ["Alice","95","A"]Integer.parseInt(parts[1])to convert Strings → intsWhitespace — Scanner"Alice 95"→ sc.next() + sc.nextInt()Scanner handles parsingand type conversion.Other — substring / regex"Alice 95 A"→ line.substring(0,10)Use .trim() to removeleading/trailing spaces.Key Conversion MethodsInteger.parseInt(s)Double.parseDouble(s)line.split(delim)These methods bridge the gap between String tokens and numeric types.split() returns a String array; parseInt/parseDouble convert individual elements.
The decision tree helps you choose the right parsing strategy based on the file's delimiter. CSV files use split(","); whitespace-delimited files let Scanner do the work; fixed-width files rely on substring().
FormatDelimiterJava Parsing ApproachWhen to Use
WhitespaceSpaces / tabs / newlinesScanner.next(), nextInt(), etc.Simple name-value pairs, integer lists
CSVComma (",")line.split(",") → String array, then Integer.parseInt()Spreadsheet exports, multi-field records
TSVTab ("\t")line.split("\t") → same strategy as CSVWhen data fields contain commas
One item per lineNewlineScanner.nextLine() in a while loopWord lists, log entries, sentences

Common text file formats and their corresponding Java parsing strategies

SECTION 6

Worked Example — Reading a CSV Into an ArrayList

Suppose you have a file called students.csv containing one record per line in the format name,gradeLevel,gpa. Your task is to read the file, store all student names with a GPA above 3.5 into an ArrayList<String>, and print the result. The file contents are:

SAMPLE FILE: students.csv

Alice,12,3.9Bob,11,3.2Carol,12,3.7Dave,10,2.8Eve,11,3.6Alice,12,3.9 Bob,11,3.2 Carol,12,3.7 Dave,10,2.8 Eve,11,3.6

Five records, each with a name (String), grade level (int), and GPA (double).

Filtering Students by GPA from a CSV File

  1. 1

    Step 1 — Import Required Classes

    You need java.io.File, java.io.FileNotFoundException, java.util.Scanner, and java.util.ArrayList. Declare your method with throws FileNotFoundException since Scanner's File constructor throws a checked exception.
    public static void main(String[] args) throws FileNotFoundException
  2. 2

    Step 2 — Open the File with Scanner

    Create a File object pointing to "students.csv", then wrap it in a Scanner. Also initialize the ArrayList that will hold the results.
    Scanner sc = new Scanner(new File("students.csv")); ArrayList<String> honors = new ArrayList<String>();
  3. 3

    Step 3 — Loop and Parse Each Line

    Use a while (sc.hasNextLine()) loop. Inside, call sc.nextLine() to get the entire line as a String, then line.split(",") to break it into a String array of three parts. Parse the GPA with Double.parseDouble(parts[2]).
    String[] parts = line.split(","); → for the first line, parts = ["Alice", "12", "3.9"]
  4. 4

    Step 4 — Apply the Filter Condition

    Check whether the parsed GPA exceeds 3.5. If so, add parts[0] (the name) to the honors ArrayList.
    if (Double.parseDouble(parts[2]) > 3.5) { honors.add(parts[0]); }
  5. 5

    Step 5 — Close Scanner and Output Results

    After the loop, close the Scanner to release the file handle. Then print the ArrayList. The expected output contains Alice (3.9), Carol (3.7), and Eve (3.6).
    sc.close(); → honors = [Alice, Carol, Eve]

The complete method is shown below for reference. Notice how concise the code is—barely ten lines of logic—yet it demonstrates every principle from Sections 2 through 5: creating a File, opening a Scanner, guarding with hasNextLine(), splitting a CSV line, converting types, filtering into a collection, and closing the resource.

COMPLETE METHOD

publicstaticvoidmain(String[]args)throwsFileNotFoundExceptionScannersc=newScanner(newFile("students.csv"));ArrayList<String>honors=newArrayList<String>();while(sc.hasNextLine())Stringline=sc.nextLine();String[]parts=line.split(",");doublegpa=Double.parseDouble(parts[2]);if(gpa>3.5)honors.add(parts[0]);sc.close();System.out.println(honors);public static void main(String[] args) throws FileNotFoundException { Scanner sc = new Scanner(new File("students.csv")); ArrayList<String> honors = new ArrayList<String>(); while (sc.hasNextLine()) { String line = sc.nextLine(); String[] parts = line.split(","); double gpa = Double.parseDouble(parts[2]); if (gpa > 3.5) { honors.add(parts[0]); } } sc.close(); System.out.println(honors); }

Output: [Alice, Carol, Eve]

SECTION 7

Strengths, Limitations & Common Pitfalls

Text files are a powerful and flexible data storage mechanism, but they are not without trade-offs. The table below contrasts the advantages of text-file I/O with its limitations, helping you decide when a text file is the right tool for the job—and when you should consider alternatives.

StrengthsLimitations
Human-readable: you can open and inspect files in any text editor.No built-in structure: you must write your own parsing logic for each format.
Platform-independent: text files work across Windows, macOS, and Linux.Slow for large data: sequential reading is O(n) and lacks random access.
No special software required: no database engine, no binary decoder.No type safety: all data is stored as characters and must be explicitly parsed.
Easy to produce: PrintWriter, System.out redirection, or even manual editing.Delimiter conflicts: commas in CSV data can break naïve split() calls.
Great for small-to-medium datasets, configuration, and logging.No concurrent access control: multiple writers can corrupt the file.

Strengths vs. limitations of text file I/O

Common Pitfalls on the AP Exam

  • Forgetting to handle FileNotFoundException — This is a checked exception. If you neither catch it nor declare it, the code will not compile.
  • Mixing nextLine() with nextInt() — After nextInt(), the newline character remains in the buffer. A subsequent nextLine() reads an empty string. Consume the leftover newline with an extra nextLine() call.
  • Not closing the Scanner — While this may not crash a small program, it leaks OS resources and is considered a defect.
  • Off-by-one with split() — Remember that split(",") returns an array whose indices start at 0. If a line has 3 fields, the valid indices are 0, 1, and 2.
✦

KEY TAKEAWAY

Think of text-file I/O like handwritten lab notes versus a digital database. Lab notes are universally readable and require no special tool, but they lack automatic indexing and error checking. Just as a scientist must carefully label and organize notebook pages, a Java programmer must carefully parse, validate, and close file resources. The simplicity of text files is both their greatest strength and their most common source of bugs.

SECTION 8

Connection to Advanced Topics

The Scanner-based file reading you learn in AP Computer Science A is the entry point to a much larger ecosystem of I/O techniques. As you advance, you will encounter buffered streams, character encodings, binary file formats, and entire frameworks for serializing objects. The table below maps AP-level concepts to their more advanced counterparts.

AP-Level ConceptAdvanced CounterpartWhy It Matters
Scanner for file readingBufferedReader + Files.lines() (Java NIO)Streams enable lazy, memory-efficient processing of massive files.
PrintWriter for file writingBufferedWriter + Files.write()Buffered writers are significantly faster for high-throughput logging and data export.
CSV parsing with split()Libraries like Apache Commons CSV, Jackson CSVHandle edge cases (quoted fields, embedded commas) that naïve splitting cannot.
Flat text filesJSON, XML, Protocol Buffers, databasesStructured formats support nesting, schema validation, and efficient querying.
scanner.close() manuallytry-with-resources (AutoCloseable)Guarantees cleanup even when exceptions occur; standard in production code.

AP concepts and their advanced counterparts

Mastering the fundamentals covered here—opening a resource, iterating through data, parsing tokens, and closing the resource—establishes a mental model that transfers directly to these advanced APIs. The specific class names change, but the underlying lifecycle pattern remains remarkably consistent across Java's entire I/O library and, indeed, across most programming languages.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Which of the following best explains why FileNotFoundException is a checked exception in Java?
  • A. Because file I/O operations are always slow and need special handling at runtime.
  • B. Because the existence of a file on disk is an external condition the compiler cannot verify, so the programmer must acknowledge and handle the possibility of failure.
  • C. Because unchecked exceptions cannot be thrown by constructors, and Scanner's constructor creates a file.
  • D. Because FileNotFoundException extends RuntimeException, which Java requires to be caught.
Show answer (B)
B is correct. Java uses checked exceptions for conditions that are reasonably foreseeable yet outside the program's control—like a missing file on disk. The compiler forces the programmer to either catch the exception or declare it with throws, ensuring the failure path is explicitly handled. A is wrong because speed has nothing to do with checked vs. unchecked exceptions. C is wrong because unchecked exceptions can be thrown by constructors (e.g., NullPointerException), and Scanner's constructor doesn't create a file—it tries to open one. D is wrong because FileNotFoundException extends IOException, not RuntimeException; RuntimeExceptions are specifically unchecked.

Question 2: BASIC

Consider the file nums.txt containing: 10 20 30 What is the output of the following code? Scanner sc = new Scanner(new File("nums.txt")); int sum = 0; while (sc.hasNextInt()) { sum += sc.nextInt(); } System.out.println(sum); sc.close();
  • A. 10
  • B. 30
  • C. 60
  • D. The code throws a NoSuchElementException.
Show answer (C)
C is correct. The while loop with hasNextInt() iterates three times, reading 10, 20, and 30. The sum is 10 + 20 + 30 = 60. A (10) would result if only one iteration occurred, which is not the case since all three tokens are valid integers. B (30) would result if only the last value were used or if the loop summed the first two and missed one—neither applies here. D is wrong because hasNextInt() returns false after the third integer, preventing any attempt to read past the end of the file.

Question 3: INTERMEDIATE

A file data.csv contains: red,5 blue,12 green,8 Consider the following code segment: Scanner sc = new Scanner(new File("data.csv")); ArrayList<String> result = new ArrayList<String>(); while (sc.hasNextLine()) { String line = sc.nextLine(); String[] parts = line.split(","); if (Integer.parseInt(parts[1]) > 6) { result.add(parts[0]); } } sc.close(); System.out.println(result); What is printed?
  • A. [red, blue, green]
  • B. [blue, green]
  • C. [blue]
  • D. [red]
Show answer (B)
B is correct. The loop reads each line, splits on the comma, and checks whether the parsed integer in parts[1] is greater than 6. For "red,5", 5 > 6 is false. For "blue,12", 12 > 6 is true, so "blue" is added. For "green,8", 8 > 6 is true, so "green" is added. The result is [blue, green]. A is wrong because it includes "red", which fails the condition. C is wrong because it omits "green" (8 > 6 is true). D is wrong because "red" (5) does not satisfy the > 6 condition.

Question 4: APPLIED

3 pts
A text file temps.txt contains one double value per line representing daily high temperatures in degrees Fahrenheit. Write a static method averageTemp that takes a String parameter filename, reads all the temperatures from the file, and returns the average as a double. If the file is empty, return 0.0. You may assume the file exists.
Show answer
Rubric (3 points): Point 1 — Correct method header and Scanner setup: Declares public static double averageTemp(String filename) throws FileNotFoundException and creates a Scanner with new Scanner(new File(filename)). Initializes accumulator variables (e.g., double sum = 0.0; int count = 0;). Point 2 — Correct loop reading and accumulating: Uses while (sc.hasNextDouble()) or while (sc.hasNextLine()) with Double.parseDouble() to read each temperature, adds it to sum, and increments count. Point 3 — Correct return value and edge case: Closes the Scanner. Returns count == 0 ? 0.0 : sum / count to handle the empty-file case without division by zero. Sample solution: public static double averageTemp(String filename) throws FileNotFoundException { Scanner sc = new Scanner(new File(filename)); double sum = 0.0; int count = 0; while (sc.hasNextDouble()) { sum += sc.nextDouble(); count++; } sc.close(); if (count == 0) return 0.0; return sum / count; }

Question 5: CRITICAL THINKING

4 pts
A school stores student records in a file roster.csv with the format lastName,firstName,gradeLevel,gpa (one record per line, no header row). Write a class RosterAnalyzer with the following: (a) A static method getHonorRoll(String filename, double minGPA) that returns an ArrayList<String> of full names (formatted as "firstName lastName") for all students whose GPA is at least minGPA. (b) A static method writeHonorRoll(String inputFile, String outputFile, double minGPA) that calls getHonorRoll and writes each name on a separate line to outputFile using a PrintWriter.
Show answer
Rubric (4 points): Point 1 — getHonorRoll: Correct file reading and parsing: Creates a Scanner on the file, loops with hasNextLine(), calls split(",") to extract parts, and correctly identifies parts[0] as lastName, parts[1] as firstName, parts[3] as GPA (parsed with Double.parseDouble()). Point 2 — getHonorRoll: Correct filtering and name formatting: Compares parsed GPA ≥ minGPA. Adds the correctly formatted name parts[1] + " " + parts[0] (firstName lastName) to the ArrayList. Returns the ArrayList. Closes Scanner. Point 3 — writeHonorRoll: Correct use of PrintWriter: Creates a PrintWriter on the output file. Iterates through the ArrayList returned by getHonorRoll and writes each name with println(). Closes the PrintWriter. Point 4 — Proper exception handling and resource management: Both methods declare throws FileNotFoundException. All Scanners and PrintWriters are closed. No resource leaks. Sample solution: import java.io.*; import java.util.*; public class RosterAnalyzer { public static ArrayList<String> getHonorRoll(String filename, double minGPA) throws FileNotFoundException { Scanner sc = new Scanner(new File(filename)); ArrayList<String> result = new ArrayList<String>(); while (sc.hasNextLine()) { String[] parts = sc.nextLine().split(","); double gpa = Double.parseDouble(parts[3]); if (gpa >= minGPA) { result.add(parts[1] + " " + parts[0]); } } sc.close(); return result; } public static void writeHonorRoll(String inputFile, String outputFile, double minGPA) throws FileNotFoundException { ArrayList<String> honors = getHonorRoll(inputFile, minGPA); PrintWriter pw = new PrintWriter(new File(outputFile)); for (String name : honors) { pw.println(name); } pw.close(); } }

SUMMARY

Summary

Summary

Text-file I/O in Java follows a consistent lifecycle: create a File object to locate the file on disk, wrap it in a Scanner to read and tokenize its contents, guard every read with hasNext-family methods to avoid exceptions, parse tokens into typed values using methods like Integer.parseInt() or Double.parseDouble(), store results in collections such as ArrayList, and always close the Scanner to release OS resources.

For files with comma-separated values, the split(",") method breaks each line into a String array, after which individual fields are accessed by index and converted as needed. The FileNotFoundException is a checked exception that must be declared or caught—Java's way of ensuring you plan for the real-world possibility that a file may not exist. Writing files mirrors reading: wrap a File in a PrintWriter, use println(), and close when done. Mastering these patterns equips you both for the AP exam and for the data-driven programming challenges that lie ahead.

Varsity Tutors • AP Computer Science A • Using Text Files