java实现大文本文件拆分

 更新时间:2020年05月19日 17:22:30   作者:lucky_白杨  
这篇文章主要为大家详细介绍了java实现大文本文件拆分,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了java实现大文本文件拆分的具体代码,供大家参考,具体内容如下

生成大文件

public static void createBigFile() throws IOException {
 File file = new File("/Users/yangpeng/Documents/temp/big_file.csv");
 FileWriter fileWriter = new FileWriter(file);
 BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
 String str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1";
 for (int i = 0; i < 1000000; i++) {
  bufferedWriter.write(str);
  bufferedWriter.newLine();
 }
 bufferedWriter.flush();
 bufferedWriter.close();
}

文件拆分

此处没有给出根据文件大小计算需要拆分的文件数量,所以这里是给定一个拆分文件数量

思路

思路:给定带拆分数量,计算出每个文件的平均字节数,然后循环文件数进行每个文件的拆分。拆分第一个文件时,根据平均字节数往后取给定的大约行字节数的字节,然后循环字节判断是否为\r或者\n,如果字节为\r或者\n则代表到达行末尾,记录行尾字节位置。知道了开头字节位置与结束字节位置,就可以将此位置之间的数据生成子文件了。继续循环拆分下个文件,基于上个文件记录的结束字节位置继续计算当前文件的结束位置,直到到达拆分文件的数量或者大文件读取完毕。

举个栗子:

有一个3行记录的文件,假设每行记录行字节包含换行符的字节数为100,也就是说这个文件的总字节数为300。

我现在要将这个文件拆分成2个。按照上面的思路,首先我需要计算出文件的平均值300/2=150,这里计算出的平均值并不是拆分出来的子文件一定是150,因为这个数字位置的字节有可能在一行的中间,那么我要基于这个数字算出下个换行符出现的位置当做我这个子文件的结束位。

所以我给定一个行字节数100+150=250,这个150到250之间的字节我认为有换行符,所以我轮询这100字节,判断是否为换行符,结果我轮到到50的位置发现了换行。

那么我这个第一个文件的结束位置是150+50=200,然后将0到200之间的字节生成第一个文件。然后基于这个200的位置继续拆分下个文件,由于200+150已经大于了源文件的大小,所以直接将200到300的数据生成一个子文件。所以最终的结果是一二行为一个子文件,三行为第二个子文件。

代码

考虑到性能与内存占用的问题,此处实现采用NIO

public static void splitFile(String filePath, int fileCount) throws IOException {
 FileInputStream fis = new FileInputStream(filePath);
 FileChannel inputChannel = fis.getChannel();
 final long fileSize = inputChannel.size();
 long average = fileSize / fileCount;//平均值
 long bufferSize = 200; //缓存块大小,自行调整
 ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.valueOf(bufferSize + "")); // 申请一个缓存区
 long startPosition = 0; //子文件开始位置
 long endPosition = average < bufferSize ? 0 : average - bufferSize;//子文件结束位置
 for (int i = 0; i < fileCount; i++) {
  if (i + 1 != fileCount) {
   int read = inputChannel.read(byteBuffer, endPosition);// 读取数据
   readW:
   while (read != -1) {
    byteBuffer.flip();//切换读模式
    byte[] array = byteBuffer.array();
    for (int j = 0; j < array.length; j++) {
     byte b = array[j];
     if (b == 10 || b == 13) { //判断\n\r
      endPosition += j;
      break readW;
     }
    }
    endPosition += bufferSize;
    byteBuffer.clear(); //重置缓存块指针
    read = inputChannel.read(byteBuffer, endPosition);
   }
  }else{
   endPosition = fileSize; //最后一个文件直接指向文件末尾
  }

  FileOutputStream fos = new FileOutputStream(filePath + (i + 1));
  FileChannel outputChannel = fos.getChannel();
  inputChannel.transferTo(startPosition, endPosition - startPosition, outputChannel);//通道传输文件数据
  outputChannel.close();
  fos.close();
  startPosition = endPosition + 1;
  endPosition += average;
 }
 inputChannel.close();
 fis.close();

}

public static void main(String[] args) throws Exception {
 Scanner scanner = new Scanner(System.in);
 scanner.nextLine();
 long startTime = System.currentTimeMillis();
 splitFile("/Users/yangpeng/Documents/temp/big_file.csv",5);
 long endTime = System.currentTimeMillis();
 System.out.println("耗费时间: " + (endTime - startTime) + " ms");
 scanner.nextLine();
}

使用NIO可以高效的实现文件拆分,我的文件为100W行大小为1.02G的文本文件,拆分成5个子文件总耗时1224ms

后如下是使用jvisualvm监控的程序内存:

可以看到拆分期间内存浮动基本在1M左右。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • IDEA报错:无效的源发行版解决方案

    IDEA报错:无效的源发行版解决方案

    很多小伙伴在刷新maven的时候总会报 Error:java:无效的源发行版,下面这篇文章主要给大家介绍了关于IDEA报错:无效的源发行版的解决方案,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • Java中stream处理中map与flatMap的比较和使用案例

    Java中stream处理中map与flatMap的比较和使用案例

    这篇文章主要介绍了Java中stream处理中map与flatMap的比较和使用案例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java编程实现高斯模糊和图像的空间卷积详解

    Java编程实现高斯模糊和图像的空间卷积详解

    这篇文章主要介绍了Java编程实现高斯模糊和图像的空间卷积详解,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • BeanUtils.copyProperties扩展--实现String转Date

    BeanUtils.copyProperties扩展--实现String转Date

    这篇文章主要介绍了BeanUtils.copyProperties扩展--实现String转Date操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java调用DOS实现定时关机的实例

    Java调用DOS实现定时关机的实例

    Java调用DOS实现定时关机的实例,需要的朋友可以参考一下
    2013-04-04
  • 利用github搭建个人maven仓库的方法步骤

    利用github搭建个人maven仓库的方法步骤

    这篇文章主要介绍了利用github搭建个人maven仓库的方法步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • 使用FeignClient调用远程服务时整合本地的实现方法

    使用FeignClient调用远程服务时整合本地的实现方法

    这篇文章主要介绍了使用FeignClient调用远程服务时整合本地的实现方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • spring启动错误Singleton bean creation not allowed while the singletons of this factory are indestruction

    spring启动错误Singleton bean creation not al

    本文主要介绍了spring启动错误Singleton bean creation not allowed while the singletons of this factory are indestruction,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java编程实现统计数组中各元素出现次数的方法

    Java编程实现统计数组中各元素出现次数的方法

    这篇文章主要介绍了Java编程实现统计数组中各元素出现次数的方法,涉及java针对数组的遍历、比较、运算等相关操作技巧,需要的朋友可以参考下
    2017-07-07
  • Java Optional实践(小结)

    Java Optional实践(小结)

    这篇文章主要介绍了Java Optional实践(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09

最新评论