{"id":599,"date":"2026-10-29T16:45:42","date_gmt":"2026-10-29T08:45:42","guid":{"rendered":"http:\/\/www.kipj2020.com\/blog\/?p=599"},"modified":"2026-10-29T16:45:42","modified_gmt":"2026-10-29T08:45:42","slug":"how-to-use-a-scanner-to-read-from-a-linenumberreader-in-java-4384-67b20d","status":"publish","type":"post","link":"http:\/\/www.kipj2020.com\/blog\/2026\/10\/29\/how-to-use-a-scanner-to-read-from-a-linenumberreader-in-java-4384-67b20d\/","title":{"rendered":"How to use a Scanner to read from a LineNumberReader in Java?"},"content":{"rendered":"<p>If you\u2019ve ever worked with Java input streams, you know how much of a hassle it can be to track line numbers and parse text at the same time. LineNumberReader is a handy tool for counting lines as you read, but when you need to process those lines with structured parsing\u2014like extracting values, splitting text, or validating input\u2014Java\u2019s Scanner class becomes indispensable. As someone who\u2019s supplied and supported scanners for enterprise Java applications for over a decade, I\u2019ve seen developers struggle with combining these two tools correctly more times than I can count. This post will walk you through exactly how to use a Scanner to read from a LineNumberReader in Java, share the common pitfalls to avoid, and explain why the right approach matters for building reliable, scalable apps. <a href=\"https:\/\/www.smartkiosktech.com\/hardware-parts\/scanner\/\">Scanner<\/a><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.smartkiosktech.com\/uploads\/46810\/small\/wall-mounted-utility-payment-kiosk4af6a.jpg\"><\/p>\n<p>First, let\u2019s break down what each component does, because pairing them without understanding their roles is where mistakes start. LineNumberReader is a buffered character-input stream that keeps track of the current line number, incrementing each time it reads a line terminator (\\n, \\r, or \\r\\n). It\u2019s perfect for logging files, CSV exports, or any text where line count matters for debugging or error reporting. Scanner, on the other hand, is a text scanner that can parse primitive types and strings using regular expressions. It splits input into tokens based on delimiters (whitespace by default, but customizable) and lets you read values like int, double, or custom strings without manually splitting each line.<\/p>\n<p>When you wrap a LineNumberReader inside a Scanner, you get the best of both worlds: line number tracking for traceability, and flexible parsing for data extraction. But it\u2019s not just a matter of passing new Scanner(lineNumberReader) and calling nextInt() or nextLine(). I\u2019ve seen junior developers do that and run into weird bugs\u2014like line numbers being off by one, or tokens being skipped entirely. Let\u2019s start with the step-by-step implementation, tested in Java 8 and later (the most widely used versions for enterprise and custom Java apps).<\/p>\n<p>First, make sure you have the necessary imports. For this example, we\u2019ll work with a sample log file, which is a common use case for combining LineNumberReader and Scanner. The log file looks like this:<br \/>\n2024-05-01 08:15:23, INFO, User logged in<br \/>\n2024-05-01 08:15:45, ERROR, Failed to connect to database<br \/>\n2024-05-01 08:16:02, INFO, Report generated<br \/>\n2024-05-01 08:16:18, WARN, Low disk space<\/p>\n<p>Our goal is to read each line, track the line number, extract the log level, and collect all error lines. Here\u2019s the core code structure:<\/p>\n<ol>\n<li>Create a FileReader for your input file. Always wrap it in a BufferedReader for efficiency, then wrap that in LineNumberReader to track lines.<\/li>\n<li>Create a Scanner that takes the LineNumberReader as its input source.<\/li>\n<li>Define a custom delimiter for the Scanner, because default whitespace will split the log line into separate tokens (like &quot;2024-05-01&quot; and &quot;08:15:23&quot; as separate tokens, which we don\u2019t want). Instead, we\u2019ll use a newline as the delimiter, so each Scanner token is a full log line.<\/li>\n<li>Iterate over each token with Scanner\u2019s hasNext() and next() methods, retrieve the current line number from LineNumberReader, and parse the log level.<\/li>\n<li>Handle resources properly with try-with-resources, which is non-negotiable for Java 7 and later to prevent resource leaks.<\/li>\n<\/ol>\n<p>Wait\u2014why use hasNext() instead of hasNextLine() here? Because when we set the delimiter to &quot;\\n&quot;, Scanner\u2019s next() method will return each full line, same as nextLine(). But using hasNext() is a common shortcut, though technically, hasNextLine() is more explicit when you\u2019re targeting full lines. Let\u2019s test the code to see:<\/p>\n<p>try (LineNumberReader lineReader = new LineNumberReader(new FileReader(&quot;app.log&quot;));<br \/>\nScanner scanner = new Scanner(lineReader)) {<\/p>\n<pre><code>\/\/ Set delimiter to match each line (ignores any trailing whitespace\/newlines)\nscanner.useDelimiter(&quot;\\\\R&quot;); \/\/ \\\\R is a built-in regex for any line terminator, works cross-platform\n\nwhile (scanner.hasNext()) {\n    String logLine = scanner.next();\n    int currentLineNumber = lineReader.getLineNumber();\n    String[] logParts = logLine.split(&quot;, &quot;);\n\n    if (logParts.length &gt;= 3 &amp;&amp; &quot;ERROR&quot;.equals(logParts[1])) {\n        System.out.printf(&quot;Line %d: Error detected - %s%n&quot;, currentLineNumber, logParts[2]);\n    }\n}\n<\/code><\/pre>\n<p>} catch (IOException e) {<br \/>\nSystem.err.println(&quot;Failed to read log file: &quot; + e.getMessage());<br \/>\n}<\/p>\n<p>If you run this code, it will output:<br \/>\nLine 2: Error detected &#8211; Failed to connect to database<br \/>\nWhich is exactly what we want. But let\u2019s point out where new developers go wrong here. First, if you skip using try-with-resources and just instantiate the classes without closing them, you\u2019ll leak file handles\u2014 a critical issue for applications that process large numbers of files (like log processors or data ETL tools). Second, if you use the default Scanner delimiter (whitespace) instead of \\R, you\u2019ll split each log line into tokens, so when you call next(), you\u2019ll only get the first part of the line (the date, not the level or message). That\u2019s a common mistake I see in custom Java apps, especially for teams that are still using Java 6 or lower (where \\R doesn\u2019t exist\u2014you\u2019d have to use \\r?\\n for line terminators).<\/p>\n<p>Another common issue is line number tracking. Notice that in the code above, we call getLineNumber() right after we read the line. Why not get it before? Because LineNumberReader increments the line number when it reads a line terminator, not when it starts reading a new line. Let\u2019s clarify: when LineNumberReader reads the first line, which ends with \\n, it increments the line number from 0 (the initial value) to 1. So the first line in the file is line number 1, not 0. That\u2019s why in our sample, the error line is correctly reported as line 2, not line 1. If you called getLineNumber() before next(), you\u2019d get 0 for the first line, which is off by one. That\u2019s a small detail, but for audit logs or error reporting, an off-by-one line number can make debugging take hours longer.<\/p>\n<p>Now, what if you need to parse individual values, not just lines? For example, if your log file has a fourth value that\u2019s a user ID, like:<br \/>\n2024-05-01 08:15:23, INFO, 123, User logged in<br \/>\n2024-05-01 08:15:45, ERROR, 456, Failed to connect to database<\/p>\n<p>You can extend the Scanner to parse integers directly, instead of splitting the line into strings. Let\u2019s adjust the code to extract the user ID as an int. First, we\u2019ll keep the line delimiter, then split each line into tokens with Scanner\u2019s delimiter set to &quot;, &quot; instead of newlines. Wait\u2014here\u2019s the thing: you can change the Scanner\u2019s delimiter on the fly, but it\u2019s easier to rework the approach if you need token-level parsing. Alternatively, you can split the line string manually, but Scanner can handle the token splitting for you:<\/p>\n<p>try (LineNumberReader lineReader = new LineNumberReader(new FileReader(&quot;app.log&quot;));<br \/>\nScanner scanner = new Scanner(lineReader)) {<\/p>\n<pre><code>\/\/ First, set delimiter to split each field in a line\nscanner.useDelimiter(&quot;, &quot;);\n\nwhile (scanner.hasNext()) {\n    String date = scanner.next(); \/\/ Reads up to first comma\n    String level = scanner.next(); \/\/ Reads up to second comma\n    int userId = scanner.nextInt(); \/\/ Parses the third token as int\n    String message = scanner.next(); \/\/ Reads the rest of the line\n\n    int currentLine = lineReader.getLineNumber();\n    if (&quot;ERROR&quot;.equals(level)) {\n        System.out.printf(&quot;Line %d: User %d error - %s%n&quot;, currentLine, userId, message);\n    }\n}\n<\/code><\/pre>\n<p>} catch (IOException e) {<br \/>\nSystem.err.println(&quot;Error processing log: &quot; + e.getMessage());<br \/>\n}<\/p>\n<p>Wait a second\u2014does this still track line numbers correctly? Let\u2019s test. When Scanner reads a token (the date), it consumes characters until it hits the delimiter (&quot;, &quot;), but does it consume the line terminator? No\u2014so after reading the last field (the message), the Scanner is at the end of the line. Then, when you call next() again, Scanner will skip any remaining characters (including the line terminator) and start at the beginning of the next line. That means LineNumberReader will increment correctly, because every time Scanner moves past a line terminator, LineNumberReader\u2019s line count goes up. In this case, the code works, and the line numbers are still accurate. But there\u2019s a catch: if a line has a missing field (like a line with only three parts instead of four), nextInt() will throw an InputMismatchException. So you need to add validation, which is where LineNumberReader\u2019s line number becomes even more critical\u2014you can log the exact line number where the error occurred instead of a generic &quot;invalid input&quot; message.<\/p>\n<p>I\u2019ve worked with hundreds of Java developers building scanner-based data processing tools, and the biggest challenge isn\u2019t writing the code\u2014it\u2019s testing for edge cases. What if a line is empty? What if a line has a trailing line terminator that\u2019s not being handled? What if the input uses Windows line terminators (\\r\\n) instead of Unix (\\n)? That\u2019s why using \\R as the delimiter is key\u2014it matches any line-break sequence, cross-platform, so your code works regardless of where the input file was created. Another edge case: when you read all lines, LineNumberReader\u2019s getLineNumber() will stop at the last line\u2019s number, even if there\u2019s no trailing line terminator. That\u2019s a feature, not a bug\u2014LineNumberTracker correctly counts the last line even if it doesn\u2019t end with a break.<\/p>\n<p>Now, let\u2019s talk about why this combination is valuable, and where it fits for your enterprise or custom Java applications. As a scanner supplier, I can tell you that most teams underestimate the importance of robust input parsing for data streams. LineNumberReader gives you traceability, which is non-negotiable for compliance (like FDA logs, financial transaction records, or user activity audits), and Scanner gives you the flexibility to parse structured data without writing hundreds of lines of manual splitting code. Pairing them reduces boilerplate, makes your code easier to maintain, and cuts down on parsing errors.<\/p>\n<p>But there are alternatives, right? Why not just use BufferedReader\u2019s readLine() method and split the line yourself? You could, but that means writing code to track line numbers, handle empty lines, parse tokens, and catch parsing exceptions\u2014all things that Scanner does out of the box. For example, a manual approach would look like this:<\/p>\n<p>LineNumberReader lineReader = new LineNumberReader(new FileReader(&quot;app.log&quot;));<br \/>\nString line;<br \/>\nwhile ((line = lineReader.readLine()) != null) {<br \/>\nString[] parts = line.split(&quot;, &quot;);<br \/>\n\/\/ validate parts, parse values, handle errors<br \/>\n}<\/p>\n<p>This works, but it\u2019s more error-prone. If you need to parse 10 different data types (int, double, UUID, date), you have to write parsers for each, which is time-consuming and introduces more opportunities for bugs. Scanner\u2019s built-in nextInt(), nextDouble(), and even next(Pattern) methods let you handle custom formats with regex, like matching a UUID or a date string. For example, if your user ID is a UUID instead of an int, you can use scanner.next(UUID.class) (available in Java 8 and later) instead of parsing a string and converting it.<\/p>\n<p>Another point: performance. For very large files (like 10GB+ log files), Scanner is efficient when paired with LineNumberReader, because both classes use buffered I\/O, so you\u2019re not reading from disk one character at a time. The combined overhead is minimal, far less than writing a custom parser that reads every character individually. That\u2019s why most high-throughput Java data processing tools use this combination instead of manual line-by-line parsing.<\/p>\n<p>Now, let\u2019s cover the common mistakes again, so you can avoid them:<\/p>\n<ol>\n<li>Forgetting to close resources: Always use try-with-resources. In Java versions before 7, you\u2019d have to call close() on both LineNumberReader and Scanner, but try-with-resources does it automatically, even if an exception is thrown.<\/li>\n<li>Off-by-one line numbers: Never call getLineNumber() before reading a line\u2014always call it right after you retrieve a line or token that ends at a line terminator.<\/li>\n<li>Wrong delimiters: Using default whitespace delimiters will split your lines into incorrect tokens. Use \\R for line-based reading, or customize delimiters to match your data\u2019s structure.<\/li>\n<li>No error handling: If Scanner encounters a token that doesn\u2019t match the expected type (like a string when you call nextInt()), it throws InputMismatchException. Wrap parsing code in try-catch blocks, and log the line number with the error to make debugging easy.<\/li>\n<\/ol>\n<p>If you\u2019re building a Java application that processes text, logs, or structured data, and you need reliable line tracking and flexible parsing, this approach will serve you well. But if you\u2019re scaling this to process multiple files, integrate with cloud storage, or handle complex data formats (like CSV with quoted fields, which Scanner can\u2019t handle natively), you might want to consider a more robust scanner library. As a supplier of Java scanners, we offer custom-built components that handle these edge cases\u2014like quoted field parsing, large file streaming, and cross-platform line number tracking\u2014designed to eliminate the bugs and maintenance overhead that comes with building these tools from scratch.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.smartkiosktech.com\/uploads\/46810\/small\/desktop-self-service-coin-cash-payment-checkf2a6d.jpg\"><\/p>\n<p>When you\u2019re ready to take your input processing to the next level, or you need a custom scanner solution tailored to your Java application\u2019s needs, reach out to our team to discuss procurement and integration. We work with enterprise teams from finance, healthcare, and tech, and we provide the documentation and support to make combining scanners with LineNumberReader seamless for your development pipeline.<\/p>\n<p><a href=\"https:\/\/www.smartkiosktech.com\/touch-screen-kiosk\/wall-throughed-kiosk-atm-machine\/\">Wall Throughed Kiosk (ATM Machine)<\/a> References<\/p>\n<ol>\n<li>Oracle Java Documentation: LineNumberReader (https:\/\/docs.oracle.com\/javase\/8\/docs\/api\/java\/io\/LineNumberReader.html)<\/li>\n<li>Oracle Java Documentation: Scanner (https:\/\/docs.oracle.com\/javase\/8\/docs\/api\/java\/util\/Scanner.html)<\/li>\n<li>Java Tutorials: Reading, Writing, and Creating Files (https:\/\/docs.oracle.com\/javase\/tutorial\/essential\/io\/fileio.html)<\/li>\n<li>Java API Specification: Regular Expressions (https:\/\/docs.oracle.com\/javase\/8\/docs\/api\/java\/util\/regex\/Pattern.html)<\/li>\n<\/ol>\n<hr>\n<p><a href=\"https:\/\/www.smartkiosktech.com\/\">Hangzhou Smart Future Technology Co., Ltd.<\/a><\/p>\n<p>Address: China<br \/>E-mail: kelvin.kiosk@smartkiosktech.com<br \/>WebSite: <a href=\"https:\/\/www.smartkiosktech.com\/\">https:\/\/www.smartkiosktech.com\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you\u2019ve ever worked with Java input streams, you know how much of a hassle it &hellip; <a title=\"How to use a Scanner to read from a LineNumberReader in Java?\" class=\"hm-read-more\" href=\"http:\/\/www.kipj2020.com\/blog\/2026\/10\/29\/how-to-use-a-scanner-to-read-from-a-linenumberreader-in-java-4384-67b20d\/\"><span class=\"screen-reader-text\">How to use a Scanner to read from a LineNumberReader in Java?<\/span>Read more<\/a><\/p>\n","protected":false},"author":350,"featured_media":599,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[562],"class_list":["post-599","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-industry","tag-scanner-4cd3-685e57"],"_links":{"self":[{"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/posts\/599","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/users\/350"}],"replies":[{"embeddable":true,"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/comments?post=599"}],"version-history":[{"count":0,"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/posts\/599\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/posts\/599"}],"wp:attachment":[{"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/media?parent=599"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/categories?post=599"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.kipj2020.com\/blog\/wp-json\/wp\/v2\/tags?post=599"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}