scala读取HDFS文件,每次读取一定的字节数

本文介绍了一个使用Scala语言从Hadoop分布式文件系统(HDFS)中逐行读取文件的方法。通过自定义对象实现了对HDFS文件的连接、读取及关闭操作,并能够按需读取文件的部分内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用scala读取一个只有字符的文件,该文件是已经清洗完的数据,有规律,一行是一条数据。
现在的需求是一次读取文件中的一部分,依次读取完。

如果有疑问,或者找出错误的解决方案,请和我联系。一起研究讨论
import java.io.IOException
import java.net.URI

import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.{FSDataInputStream, FileSystem, Path}

/**
  * Created by wx on 2017/7/20.
  */
object HDFSUtil {
  val conf: Configuration = new Configuration
  var fs: FileSystem = null
  var hdfsInStream: FSDataInputStream = null

  def getFSDataInputStream(path: String): FSDataInputStream = {
    try {
      fs = FileSystem.get(URI.create(path), conf)
      hdfsInStream = fs.open(new Path(path))
    } catch {
      case e: IOException => {
        e.printStackTrace
      }
    }
    return hdfsInStream
  }

  def close {
    try {
      if (hdfsInStream != null) {
        hdfsInStream.close
      }
      if (fs != null) {
        fs.close
      }
    }
    catch {
      case e: IOException => {
        e.printStackTrace
      }
    }
  }
}

import java.util
import org.apache.hadoop.fs.FSDataInputStream

/**
  * 用scala读取一个只有字符的文件,该文件是已经清洗完的数据,有规律,一行是一条数据。
  * 现在的需求是一次读取文件中的一部分,依次读取完。
  * Created by wx on 2017/7/21.
  */
object ReadHDFSFile {
  def main(args: Array[String]) {
    var inputStream: FSDataInputStream = null

    try {
      inputStream = HDFSUtil.getFSDataInputStream("hdfs://master:9000/csw/TestData/aviation9/part-00000")
      //每次读取的字节长度
      val readLength = 1024
      //创建一个字节数组
      var ioBuffer: Array[Byte] = new Array[Byte](readLength)
      //先读取一个1024字节的数据
      var readLen = inputStream.read(ioBuffer)
      //因为每次读取1024个字节,会遇到一行读取一半,剩下的一半下一次读取。
      //在这设置个临时变量来存放前面读取的一半,然后和后面读取到的拼接起来
      var tmp = ""
      //只做一个计数
      var count = 1
      while (readLen != -1) {
        //将读取到字节转成字符串
        var str = new String(ioBuffer, 0, readLen)
        /*这里是处理,当读取中文时,因为中文是两个字节,回读取一半,
        这样在转码的时候就会报错,当遇到这样的情况,需要再往后读取一个字节,然后和之前的组成新的字节数组
        这样可以避免出现读取一半的情况,但是我的数据量很多,即使这样处理,还是会偶尔遇到乱码,
        最后我又改进了,一次读取一行。*/
        val b2: Array[Byte] = str.getBytes("utf8")
        if (b2(b2.length - 1) == -67) {
          var b3: Array[Byte] = new Array[Byte](1)
          inputStream.read(b3);
          val temp: Byte = b3(0)
          b3 = util.Arrays.copyOf(ioBuffer, b3.length + ioBuffer.length)
          b3(b3.length - 1) = temp;
          str = new String(b3)
        }
        //按换行符切割
        val arr = str.split("\n")
        val arrLength = arr.length
        //获取数据还剩多少字节可读
        var available = inputStream.available()
        println(available + "剩余可读字节数")
        //在这个里进行数据的判断,将一整行数据输出,将不完整的数据进行存放和拼接
        for (i <- 0 until arrLength) {
          if (i == 0) {
            println(count + "\t" + tmp + arr(i) + "\t我再这")
            count += 1
          } else if (i == arrLength - 1 && available != 0) {
            tmp = arr(arrLength - 1)
          } else {
            println(count + "\t" + arr(i) + "\t我再这")
            count += 1
          }
        }
        //判断当可读字节小于一开始设定的字节
        if (available < readLength) {
          //最后一次读取,按照未读的字节的长度新建一个byte数组,这样可以避免最后会重复读取数据。
          ioBuffer = new Array[Byte](available)
          readLen = inputStream.read(ioBuffer)
        } else {
          readLen = inputStream.read(ioBuffer)
        }
        //让程序暂停睡眠一下
        Thread.sleep(500)
      }
    } catch {
      case e: Exception => {
        e.printStackTrace()
      }
    } finally {
      if (inputStream != null) {
        HDFSUtil.close
      }
    }
  }
}



<think>我们正在解决Spark读取CSV文件时出现中文乱码的问题。根据引用内容,乱码的主要原因是文件编码与Spark默认的UTF-8编码不一致,例如文件可能是GBK或GB2312编码。 解决方案总结: 1. 在读取CSV文件时,通过`.option("encoding", "gbk")`(或其他正确编码)指定文件编码。 2. 如果使用`spark.read.textFile`读取文本文件,也需要指定编码,但注意`textFile`方法没有直接提供设置编码的option,可能需要使用其他方式(如使用`spark.read.option("encoding", "gbk").text(path)`)。 3. 如果上述方法无效,可以尝试使用Hadoop API读取,然后手动转换编码(如引用[4]所示)。 具体步骤: 方法一:使用Spark DataFrame API读取CSV时指定编码(推荐) ```scala val df = spark.read .option("header", "true") // 如果第一行是标题 .option("encoding", "gbk") // 根据文件实际编码设置,如GBK, GB2312等 .option("delimiter", ",") // 分隔符,根据实际情况调整 .csv("path/to/your/file.csv") ``` 方法二:读取文本文件(非结构化)时指定编码 ```scala val textDF = spark.read .option("encoding", "gbk") .text("path/to/your/file.txt") ``` 方法三:使用Hadoop API读取并转换编码(当上述方法无效时) 这种方法适用于需要逐行处理且编码问题复杂的情况。注意,这种方法会得到RDD,然后可以转换为DataFrame。 ```scala import org.apache.hadoop.io.{LongWritable, Text} import org.apache.hadoop.mapreduce.lib.input.TextInputFormat import org.apache.spark.rdd.RDD // 使用newAPIHadoopFile读取,指定编码 val rdd: RDD[String] = spark.sparkContext .newAPIHadoopFile( "path/to/your/file.csv", classOf[TextInputFormat], classOf[LongWritable], classOf[Text], spark.sparkContext.hadoopConfiguration ) .map { case (_, text) => // 这里将Text转换为String,并指定编码转换 new String(text.getBytes, 0, text.getLength, "GBK") } // 将RDD转换为DataFrame val df = spark.createDataFrame(rdd.map(Tuple1.apply)).toDF("column_name") ``` 注意:在方法三中,我们通过`new String(text.getBytes, 0, text.getLength, "GBK")`将原始字节按指定编码转换为字符串。这里需要确保指定的编码与实际文件编码一致。 为什么有时指定encoding选项无效? 根据引用[4]中的描述,可能在某些情况下(如使用某些Hadoop版本或文件系统)Spark的CSV读取器对编码的支持不够好。此时使用Hadoop API直接读取并转换编码是更可靠的方法。 另外,还需要注意: - 如果文件是BOM头(如UTF-8 with BOM),可能需要先去除BOM头,或者使用支持BOM的编码(如"UTF-8-BOM"),但Java标准库并不直接支持,可能需要手动处理前几个字节。 - 如果文件混合了多种编码,则处理起来会更复杂,可能需要逐行判断或使用工具统一编码。 建议:在读取前,尽量将文件转换为UTF-8编码(无BOM),这样可以避免很多编码问题。 相关问题: 1. Spark读取CSV文件时如何自动检测文件编码? 2. 如何处理包含多种编码的CSV文件? 3. 如何解决Spark读取Excel文件时的中文乱码问题? 4. 如何将GBK编码的CSV文件批量转换为UTF-8编码?</think>### Spark 读取 CSV 文件乱码原因及解决方法 #### 一、乱码原因分析 1. **编码不匹配**(核心原因) Spark 默认使用 `UTF-8` 编码读取文件,当 CSV 文件使用 `GBK`/`GB2312` 等中文编码时,会导致乱码[^1][^2][^3]。 $$ \text{乱码} = \text{解码器}(\text{GBK字节流} \mid \text{UTF-8规则}) $$ 2. **Hadoop 配置问题** 某些 Hadoop 环境默认使用 `org.apache.hadoop.io.Text` 的默认编码(通常为 UTF-8),无法自动识别源文件编码[^4]。 3. **特殊字符处理** 包含 BOM 头(如 `EF BB BF`)或混合编码的文件可能导致解析异常。 --- #### 二、解决方案(按优先级排序) ##### ✅ 方案1:直接指定编码(推荐) 在 `spark.read` 时显式声明文件编码: ```scala // Scala 示例 val df = spark.read .option("header", "true") // 包含标题行 .option("encoding", "gbk") // ⭐关键参数:指定GBK编码 .option("delimiter", ",") // 分隔符(根据实际情况调整) .csv("hdfs:///path/to/file.csv") ``` ```python # PySpark 示例 df = spark.read \ .option("header", "true") \ .option("encoding", "gbk") \ # 关键参数 .option("sep", ",") \ .csv("hdfs:///path/to/file.csv") ``` ##### ✅ 方案2:使用二进制读取 + 手动解码 当方案1失效时(如旧版本 Spark),使用 Hadoop API: ```scala import org.apache.hadoop.io.{LongWritable, Text} import org.apache.spark.rdd.RDD val rawRDD: RDD[String] = spark.sparkContext .hadoopFile("hdfs:///path.csv", classOf[TextInputFormat], classOf[LongWritable], classOf[Text]) .map { case (_, text) => new String(text.getBytes, 0, text.getLength, "GBK") // ⭐手动GBK解码 } // 转换为DataFrame val df = spark.read.option("delimiter", ",").csv(rawRDD.toDS()) ``` ##### ✅ 方案3:全局编码配置(集群环境) 在 `spark-submit` 中设置默认编码: ```bash spark-submit \ --conf spark.hadoop.hadoop.tmp.dir=/tmp \ --conf spark.hadoop.mapreduce.input.fileinputformat.input.dir.recursive=true \ --conf spark.hadoop.fs.file.impl.disable.cache=true \ --conf spark.hadoop.fs.defaultFS=hdfs://cluster \ --conf spark.executor.extraJavaOptions="-Dfile.encoding=GBK" \ # ⭐全局编码 --conf spark.driver.extraJavaOptions="-Dfile.encoding=GBK" \ your_app.jar ``` --- #### 三、编码检测技巧 1. **Linux 文件编码检测** ```bash file -i input.csv # 输出示例:input.csv: text/plain; charset=gbk ``` 2. **Python 自动检测** ```python import chardet with open("local.csv", "rb") as f: result = chardet.detect(f.read(10000)) print(result["encoding"]) # 输出:GBK ``` --- #### 四、避免 BOM 问题 在方案2基础上添加 BOM 头处理: ```scala rawRDD.map { line => if(line.startsWith("\uFEFF")) line.substring(1) // 移除UTF-8 BOM头 else line } ``` --- #### 五、根本解决方案建议 1. **源文件转码**: 使用 `iconv` 批量转换编码(适合静态数据): ```bash iconv -f GBK -t UTF-8 source.csv > target_utf8.csv ``` 2. **统一数据管道**: 在数据接入层强制所有 CSV 转为 UTF-8 编码存储(推荐生产环境使用)。 --- ### 相关问题 1. Spark 读取 JSON 文件时出现乱码如何解决? 2. 如何批量检测 HDFS文件的编码类型? 3. Spark 读取 Excel 文件时如何避免中文乱码? 4. 分布式环境下如何统一所有节点的文件编码配置?[^2][^3]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值