内存文件映射方式读取超大文件踩坑题解析

问:使用内存文件映射 MappedByteBuffer 读超大文件会有什么问题吗?

答:这种方式存在一个致命问题就是依然没法读取超大文件(大于 Integer.MAX_VALUE),因为 FileChannel 的 map 方法中 size 参数会有大小限制,源码中发现该参数值大于 Integer.MAX_VALUE 时会直接抛出 IllegalArgumentException(“Size exceeds Integer.MAX_VALUE”) 异常,所以对于特别大的文件其依然不适合。

本质上是由于 java.nio.MappedByteBuffer 直接继承自 java.nio.ByteBuffer ,而 ByteBuffer 的索引是 int 类型的,所以 MappedByteBuffer 也只能最大索引到 Integer.MAX_VALUE 的位置,所以 FileChannel 的 map 方法会做参数合法性检查。

我们可以通过多个内存文件映射来解决这个问题,具体如下。

class BigMappedByteBufferReader {
    private MappedByteBuffer[] mappedByteBuffers;
    private FileInputStream inputStream;
    private FileChannel fileChannel;

    private int bufferCountIndex = 0;
    private int bufferCount;

    private int byteBufferSize;
    private byte[] byteBuffer;

    public BigMappedByteBufferReader(String fileName, int byteBufferSize) throws IOException {
        this.inputStream = new FileInputStream(fileName);
        this.fileChannel = inputStream.getChannel();
        long fileSize = fileChannel.size();
        this.bufferCount = (int) Math.ceil((double) fileSize / (double) Integer.MAX_VALUE);
        this.mappedByteBuffers = new MappedByteBuffer[bufferCount];
        this.byteBufferSize = byteBufferSize;

        long preLength = 0;
        long regionSize = Integer.MAX_VALUE;
        for (int i = 0; i < bufferCount; i++) {
            if (fileSize - preLength < Integer.MAX_VALUE) {
                regionSize = fileSize - preLength;
            }
            mappedByteBuffers[i] = fileChannel.map(FileChannel.MapMode.READ_ONLY, preLength, regionSize);
            preLength += regionSize;
        }
    }

    public synchronized int read() {
        if (bufferCountIndex >= bufferCount) {
            return -1;
        }

        int limit = mappedByteBuffers[bufferCountIndex].limit();
        int position = mappedByteBuffers[bufferCountIndex].position();

        int realSize = byteBufferSize;
        if (limit - position < byteBufferSize) {
            realSize = limit - position;
        }
        byteBuffer = new byte[realSize];
        mappedByteBuffers[bufferCountIndex].get(byteBuffer);

        //current fragment is end, goto next fragment start.
        if (realSize < byteBufferSize && bufferCountIndex < bufferCount) {
            bufferCountIndex++;
        }
        return realSize;
    }

    public void close() throws IOException {
        fileChannel.close();
        inputStream.close();
        for (MappedByteBuffer byteBuffer: mappedByteBuffers) {
            byteBuffer.clear();
        }
        byteBuffer = null;
    }

    public synchronized byte[] getCurrentBytes() {
        return byteBuffer;
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        BigMappedByteBufferReader reader = new BigMappedByteBufferReader("superbig.txt", 1024);
        while (reader.read() != -1) {
            byte[] bytes = reader.getCurrentBytes();
            //超大文件搞事情
            System.out.println(new String(bytes));
        }
        reader.close();
    }
}

如上便是一种解决方案,其实质依然是分割。

原文链接

https://mp.weixin.qq.com/s/m7UyPbSQelWaKiC6Im85Bw

<think>嗯,用户想了解C#读取CSV文件的方法。查阅提供的三个引用,发现方法挺多样的:引用[1]提到了System.IO基础类、CsvHelper库和LINQ;引用[2]展示了用OPENROWSET的数据库式读取;引用[3]则演示了手动解为DataTable的流程。 用户可能是需要实际操作的指导,应该优先推荐主流可靠的方式。CsvHelper库虽然第三方但最专业,尤其适合复杂CSV;而基础StreamReader最可控但需要处理逗号转义等细节问题。数据库方式反而显得有点过时了,除非用户正好在整合ETL流程。 注意到上次回答时用户没提具体场景,这次可以补充些典型用例:中小文件用File.ReadAllLines最快捷,百万行以上用CsvHelper性能更好,需要内存表时DataTable也不错。安全方面要提醒用户注意字段引号和分隔符陷阱,比如字段内包含逗号的情况。 最后生成的问题要覆盖进阶需求:性能对比能帮助决策,异常处理是实战关键,而格式规范问题多数用户会。特别要强调CsvHelper解决编码问题的优势,很多用户会遇到乱码却不知道原因。 </think>以下是C#读取CSV文件的常用方法总结,结合代码示例说明: ### 一、基础方法(System.IO) ```csharp // 方法1:StreamReader逐行解 using System.IO; var path = "data.csv"; List<string[]> rows = new List<string[]>(); using (var reader = new StreamReader(path)) { while (!reader.EndOfStream) { var line = reader.ReadLine(); var values = line.Split(','); // 注意:需处理带引号的字段 rows.Add(values); } } ``` > **适用场景**:小型文件、简单格式[^1] ### 二、专业库处理(CsvHelper) ```csharp // 方法2:使用NuGet包CsvHelper(推荐) using CsvHelper; using System.Globalization; var records = new List<MyClass>(); using (var reader = new StreamReader("data.csv")) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) { records = csv.GetRecords<MyClass>().ToList(); } // 定义数据模型 public class MyClass { public int Id { get; set; } public string Name { get; set; } } ``` > **优势**:自动类型转换、处理特殊字符、支持映射[^1] ### 三、数据库式读取(OleDb) ```csharp // 方法3:通过OleDB连接 string connString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Data;Extended Properties='text;HDR=Yes;FMT=Delimited'"; using (var conn = new OleDbConnection(connString)) { conn.Open(); var cmd = new OleDbCommand("SELECT * FROM data.csv", conn); OleDbDataAdapter adapter = new OleDbDataAdapter(cmd); DataTable dt = new DataTable(); adapter.Fill(dt); // CSV数据加载到DataTable } ``` > **注意**:需安装AccessDatabaseEngine[^2] ### 四、LINQ便捷操作 ```csharp // 方法4:LINQ快速处理 var csvLines = File.ReadAllLines("data.csv") .Skip(1) // 跳过标题行 .Select(line => line.Split(',')) .Where(fields => fields.Length == 3) .Select(fields => new { ID = int.Parse(fields[0]), Name = fields[1], Value = decimal.Parse(fields[2]) }); ``` ### 📌选择建议: | 方法 | 适用场景 | 复杂度 | |---------------|----------------------------------|--------| | CsvHelper | 大型文件/复杂结构/类型转换 | ★★☆ | | StreamReader | 小型文件/自定义解 | ★☆☆ | | OleDb | 需SQL查询/与其他数据源联合 | ★★☆ | | LINQ | 内存操作/简单过滤转换 | ★☆☆ | ### ⚠️注意事项: 1. **字段引号处理**:当字段包含逗号时(如:`"Smith, John"`),需特殊处理 2. **编码问题**:使用`StreamReader(path, Encoding.UTF8)`指定编码 3. **性能优化**:大文件建议使用`TextFieldParser`类(`Microsoft.VisualBasic.FileIO`命名空间) > 引用说明:基础方法参考[^1],数据库连接方式引用[^2],内存表构建参考[^3] --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值