文件读写——全文单词统计

28 阅读3分钟
object words01 {
    def main(args: Array[String]): Unit = {
        // 1. 读取文件内容
        val content = scala.io.Source.fromFile("test.txt").mkString
        
        // 2. 把字符串拆分为单词
        // 使用正则表达式 \\W+ 去拆分字符串,\\W 表示非单词字符(!, ., 空格等)
        val wordList = content.split("\\W+")
            .map(_.toLowerCase()) // 转换为小写
            .filter(_.nonEmpty)   // 过滤空字符串
        
        // 3. 统计每个单词出现的次数
        val map1 = scala.collection.mutable.Map[String, Int]()
        
        // 对于每个单词进行统计
        wordList.foreach(word => {
            if (map1.contains(word)) {
                map1(word) += 1 // 存在:把它的值+1
            } else {
                map1(word) = 1 // 不存在:把它的值设为1
            }
        })
        
        // 4. 对结果排序(降序排列)
        // Map本身是无序的,把它转换成List进行排序
        val sortedWordList = map1.toList
            .sortBy(_._2) // 根据元组的第二个值(词频)排序
            .reverse      // 翻转,变为降序
        
        // 5. 打印结果
        sortedWordList.foreach(e1 => println(e1))
    }
}

Scala 文件读写与单词统计概要

一、文件读取

1. 基本文件读取方法

scala

import scala.io.Source

// 1. 读取整个文件为字符串
val content: String = Source.fromFile("test.txt").mkString

// 2. 逐行读取
val lines: Iterator[String] = Source.fromFile("test.txt").getLines()
val lineList: List[String] = lines.toList

// 3. 读取为字符数组
val chars: Array[Char] = Source.fromFile("test.txt").toArray

2. 带编码的文件读取

scala

// 指定编码格式
Source.fromFile("test.txt", "UTF-8").mkString

3. 资源安全读取(自动关闭)

scala

import scala.util.Using

Using.resource(Source.fromFile("test.txt")) { source =>
    source.mkString
}

二、文件写入

1. 使用 java.io 包

scala

import java.io.{PrintWriter, File}

// 1. 基本写入
val writer = new PrintWriter(new File("output.txt"))
writer.write("Hello, World!")
writer.close()

// 2. 使用 try-finally 确保关闭
val writer2 = new PrintWriter(new File("output.txt"))
try {
    writer2.write("Content")
} finally {
    writer2.close()
}

2. 使用 Scala 工具类

scala

import scala.sys.process._

// 将字符串写入文件
"Hello World" #> new java.io.File("output.txt") !

三、单词统计完整流程

1. 基本流程代码

scala

object WordCount {
    def main(args: Array[String]): Unit = {
        // 1. 读取文件
        val content = scala.io.Source.fromFile("test.txt").mkString
        
        // 2. 清洗和分割单词
        val words = content.toLowerCase()          // 转为小写
            .replaceAll("[^a-zA-Z0-9\s]", " ")   // 替换标点为空格
            .split("\s+")                        // 按空白字符分割
            .filter(_.nonEmpty)                   // 过滤空字符串
        
        // 3. 统计词频(多种方式)
        
        // 方式1:使用可变Map(传统命令式)
        val map1 = scala.collection.mutable.Map[String, Int]()
        words.foreach { word =>
            map1(word) = map1.getOrElse(word, 0) + 1
        }
        
        // 方式2:使用不可变Map(函数式)
        val map2 = words.groupBy(identity)       // 按单词分组
            .mapValues(_.length)                // 统计每组数量
        
        // 方式3:使用foldLeft
        val map3 = words.foldLeft(Map[String, Int]()) { (acc, word) =>
            acc + (word -> (acc.getOrElse(word, 0) + 1))
        }
        
        // 4. 排序输出
        val sorted = map2.toList
            .sortBy(-_._2)  // 按词频降序
            .take(10)       // 取前10个
        
        // 5. 输出结果
        println("Top 10 words:")
        sorted.foreach { case (word, count) =>
            println(s"$word: $count")
        }
        
        // 6. 写入文件
        val writer = new java.io.PrintWriter("result.txt")
        sorted.foreach { case (word, count) =>
            writer.println(s"$word\t$count")
        }
        writer.close()
    }
}

2. 优化版本(模块化设计)

scala

object AdvancedWordCount {
    
    // 读取文件
    def readFile(filename: String): String = {
        scala.io.Source.fromFile(filename).mkString
    }
    
    // 清洗文本
    def cleanText(text: String): String = {
        text.toLowerCase()
            .replaceAll("[^a-zA-Z0-9\s]", " ")
            .replaceAll("\s+", " ")
    }
    
    // 分割单词
    def splitWords(text: String): Array[String] = {
        text.split("\s+").filter(_.nonEmpty)
    }
    
    // 统计词频
    def countWords(words: Array[String]): Map[String, Int] = {
        words.groupBy(identity).mapValues(_.length)
    }
    
    // 排序结果
    def sortResults(wordCounts: Map[String, Int]): List[(String, Int)] = {
        wordCounts.toList.sortBy(-_._2)
    }
    
    // 写入结果
    def writeResults(results: List[(String, Int)], filename: String): Unit = {
        val writer = new java.io.PrintWriter(filename)
        results.foreach { case (word, count) =>
            writer.println(f"$word%-15s $count%d")
        }
        writer.close()
    }
    
    def main(args: Array[String]): Unit = {
        // 执行流程
        val content = readFile("test.txt")
        val cleaned = cleanText(content)
        val words = splitWords(cleaned)
        val counts = countWords(words)
        val sorted = sortResults(counts)
        
        // 输出到控制台
        sorted.take(20).foreach(println)
        
        // 保存到文件
        writeResults(sorted, "wordcount_result.txt")
    }
}

四、实用技巧

1. 处理大文件

scala

// 流式处理,避免内存溢出
val wordCounts = scala.io.Source.fromFile("large.txt")
    .getLines()
    .flatMap(line => line.toLowerCase().split("\W+"))
    .filter(_.nonEmpty)
    .foldLeft(Map[String, Int]()) { (map, word) =>
        map + (word -> (map.getOrElse(word, 0) + 1))
    }

2. 忽略停用词

scala

val stopWords = Set("the", "a", "an", "and", "or", "but", "in", "on", "at")

val filteredCounts = wordCounts.filterNot {
    case (word, _) => stopWords.contains(word)
}

3. 多文件统计

scala

val files = List("file1.txt", "file2.txt", "file3.txt")

val totalCounts = files.flatMap { file =>
    scala.io.Source.fromFile(file)
        .mkString
        .split("\W+")
        .map(_.toLowerCase)
        .filter(_.nonEmpty)
}.groupBy(identity)
 .mapValues(_.length)

五、Spark 版本(大数据处理)

scala

import org.apache.spark.{SparkConf, SparkContext}

object SparkWordCount {
    def main(args: Array[String]): Unit = {
        val conf = new SparkConf().setAppName("WordCount")
        val sc = new SparkContext(conf)
        
        // 读取文件
        val textFile = sc.textFile("hdfs://path/to/largefile.txt")
        
        // 单词统计
        val counts = textFile.flatMap(line => line.split(" "))
            .map(word => (word.toLowerCase(), 1))
            .reduceByKey(_ + _)
        
        // 保存结果
        counts.saveAsTextFile("hdfs://path/to/output")
        
        sc.stop()
    }
}

六、总结要点

  1. 文件读取:优先使用 Source.fromFile,大文件使用流式处理

  2. 文本清洗:注意大小写转换和标点符号处理

  3. 统计方法

    • 小数据:使用 groupBy + mapValues
    • 大数据:使用 foldLeft 或 Spark
  4. 结果输出:注意排序和格式化

  5. 资源管理:确保文件读写后正确关闭