登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  java教程

Java LineNumberReader 设置行号后为什么不会跳行

来源:17golang原创

时间:2026-10-29 05:23:14 366浏览 收藏

LineNumberReader.setLineNumber(2)只把行号计数器设为2,不会把读取位置移动到第三行。要取文件第3行,必须实际读过前2行,再调用一次readLine()。给日志调整显示编号与按行定位是两种不同的需求。

官方地址:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/LineNumberReader.html

用读取次数建立基线

假设输入依次是alpha、beta、gamma三行,每行都以换行符结束。目标是获取gamma。先记录两个指标:调用了多少次readLine,以及实际返回什么文本。不要只看getLineNumber,因为这个值可以被应用主动改写。

下面是供读者复现的独立Java示例,未在本文环境运行;输出说明来自API语义推导,不是压测记录。

import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;

public class LineNumberExample {
    static String readTarget(LineNumberReader reader, int target) throws IOException {
        // 本方法要求新建且未消费的reader,以文件首行为第1行。
        if (target 

第一个reader预期返回alpha,接着打印计数3;另一个从头读取的reader预期返回gamma。前者计数看起来已到3,内容却仍是第一行,这正是误判来源。

分清计数器与读取位置

把LineNumberReader看成同时维护两类信息的包装器:读取状态决定下次能取得什么字符,行号计数用于报告当前编号。setLineNumber只关联后者;getLineNumber也不会读取数据。实际读取由readLine等读取方法完成。

在上述例子中,setLineNumber(2)使后续编号以2为基准累计。第一次readLine消费alpha及其终止符,计数成为3;它并没有因为计数基准改变而跨过alpha或beta。这个功能适合为已截取的文本片段补充编号基准,但不能代替文件定位。

计数接口与字符读取状态的静态关系
结构说明图:计数分组中的方法关联行号值,读取分组中的方法关联字符状态;分组说明职责,不表示执行顺序。

顺序读取到目标行

示例的readTarget采用明确的起点约定:调用方传入刚创建、尚未消费的reader,target从1开始。循环消费target减1行,最后再取一行。目标为1时循环不执行;文件不足目标行数时返回null。空白行返回空字符串,仍是一行,不能当作文件结束。

这个辅助方法用独立局部变量row计算物理文本行,因此不会拿可被重设的getLineNumber作为停止条件。若reader已经读取过部分内容,应重新打开文件,或另行设计“相对于当前位置”的接口;不要把已消费的reader交给这个承诺从首行定位的方法。

真实文件可用Files.newBufferedReader并明确字符集,再包一层LineNumberReader,仍采用try-with-resources关闭最外层。不要在包装器读取期间绕过它去消费底层Reader;缓冲和计数状态会失去一致的解释基础。

用内容与操作数对照方案

方案到目标3的readLine调用返回内容含义
先设计数2,再读一次1次alpha修改编号,未定位
从头跳过2行,再读一次3次gamma消费前缀,取得目标

这张表是固定输入下的操作数量推导,不是耗时或吞吐量测试。顺序读取目标N需要消费前N行对应的字符,成本取决于前缀总字符数,不能把“设置一次计数”宣传成常数时间行定位。

如果要自行比较耗时,应固定文件内容、字符集、目标行、JDK和存储条件,并区分冷热缓存、预热和重复次数,同时核对返回文本是否正确。只比较行号值或两段不同语义代码的耗时,没有业务意义。反复随机读取超大文件时,可以考虑另行构建索引,但字节偏移、字符编码和换行规则需要一起设计;本例不提供随机访问索引实现。

目标行读取的参数、资源与返回值关系
结构说明图:从头读取约定约束reader,目标行约束辅助方法,返回值区分文本与不存在;连线只解释静态契约。

处理换行、回读和资源边界

LineNumberReader识别LF、CR和CRLF,CRLF作为一个行终止符。readLine返回的字符串不包含行终止符。末尾没有终止符但仍有文本时,该文本仍可构成最后一行;空文件则直接返回null。不要用字符串长度反推行数,也不要以为每行固定占用相同字节。

若只想临时读取后回到已知位置,可在标记有效范围内配合mark与reset;reset也会恢复相应行号。标记可能因超出readAheadLimit而失效,此限制按字符考虑,并不是任意行号书签。它与setLineNumber的用途不同。

skip能不能直接跳过指定行数?

skip的参数表达字符数量,不是行数。行长度和换行形式不同,不能直接把行号作为参数。

为什么刚创建时行号是0?

默认计数从0开始,是读取进展的计数基准。若业务把下一条未读记录称为第1行,应明确这个展示规则,不要为了显示编号提前改动读取位置的假设。

只要读取指定行,必须用LineNumberReader吗?

不必须。BufferedReader也可以逐行消费并用局部变量计数。只有需要读取过程中维护或调整行号时,LineNumberReader才提供额外便利。

判断改动是否正确,先核对返回内容,再解释行号。应用要跳到某行,就实际消费文本;应用只要调整编号基准,才调用setLineNumber。

参考:Java SE 21 LineNumberReader API

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>