CSV文件和XLS文件有何区别

今天在做一个需求时,需要将统计后的结果写入到HDFS上,方便后续业务分析,能够读取到该文件,以邮件附件的形式展示文件的结果。但是发现,最后展示的CSV格式的文件作为邮件附件是没有问题的,但是XLS的文件却出现了很多乱码,如图:
CSV文件和XLS文件有何区别_第1张图片
日报乱码.png

读取csv、xls文件的方法如下:

val bytes = HdfsUtil.readHdfsFileToByteArray(AttachmentSummary.HDFS_OUTPUT + day +".xls")
val attaches =Map[String, Array[Byte]](s"附件-$day.xls" -> bytes)

其中readHdfsFileToByteArray方法如下,利用InputStreamReader 去读取文件:

val fs: FileSystem = FileSystem.get(CONF)
...
  def readHdfsFileToByteArray2(fileName: String): Array[Byte] = {
    //val file = new File(fileName)
    var reader: InputStreamReader = null
    try{
      val finput = fs.open(new Path(fileName))
      reader = new InputStreamReader(finput)
      IOUtils.toByteArray(reader)   // use commons.io.IOUtils 
    } finally {
      IOUtils.closeQuietly(reader)  
    }
  }

后来查询相关的资料才发现,csv属于文本文件,而xls是二进制文件,二者有本质区别。
CSV文件和XLS文件有何区别_第2张图片
iostream.png

Reader是用来处理字符流的,所以csv文件是没有问题的,但是用来处理字节流是不行的,所以xls文件乱码。
后续改为使用ByteArrayOutputStream直接拷贝一个inputStream,这样xls文件是OK的:

  def readHdfsFileToByteArray(fileName: String): Array[Byte] = {
    val finput = fs.open(new Path(fileName))
    val output = new ByteArrayOutputStream()
    try{
      IOUtils.copy(finput, output)
      output.toByteArray
    } finally {
      finput.close()
      output.close()
    }
  }

总结:

  1. 文本文件。像txt,html, csv等格式的文件都属于文本文件。可以使用字符流来处理。
  2. 二进制文件。像xls,word等文件都是二进制文件。若用流来处理,需使用字节流。
  3. 字节流操作的基本单元为字节,通常用于处理二进制数据;字符流操作的基本单元为Unicode码元,通常用于处理文本数据。
  4. 文本文件为了兼容性,最好在写入时,加上BOM头 ByteOrderMark.UTF_8.getBytes,这样打开时,将严格按照指定的编码方式打开,否则不同平台,可能出现乱码。而二进制文件,不存在BOM头一说,只要一个字节写错,整个文件都将乱码。
  5. 判断某文件是文本还是二进制文件,能用NotePad打开不是乱码的是文本文件,打开全是乱码的则是二进制文件。而且对于文本文件,还可以使用Nodepad来转换格式,加BOM头等操作。

参考资料:
理解Java中字符流与字节流的区别
Java 流(Stream)、文件(File)和IO

你可能感兴趣的:(CSV文件和XLS文件有何区别)