R: R CSV 文件读写:readr 完整教程

最后更新:2026-08-26

前面 10 课我们学的都是 R 内部数据,但真实项目里 99% 的数据在外部文件里。这一课我们学最常用的数据格式——CSV 文件。R 有两套 CSV 读写方案:基础 R 的 read.csv 和 tidyverse 的 readr,后者更智能、更快。

读完这一课你就能把 @,000 rows of的销售数据5 秒读进 R,并写出干净的报表。

1. 你将学到



2. 一个数据导入的痛点

(1) 痛点:Excel 转 R 卡死

小陈是分析师,manager发了 1 个 100 MB 的销售 CSV:

TEXT 📖 仅展示
id,date,product,region,amount
1,2024-01-15,手机,北京,2999
2,2024-01-15,电脑,上海,5999
3,2024-01-16,手机,广州,2899
...
(@,000 rows of)

他用 read.csv() 读取,等了 30 秒,打印出 @,000 rows of又花了 10 秒,还发现 5 列全是 chr 类型(数字当字符串了)。

(2) R 的解法

R
# 1. 用 readr 智能读取(5 秒)
library(readr)
sales <- read_csv("sales.csv")

# 2. 自动类型推断
# id → integer, date → date, product → character,
# region → character, amount → double

# 3. 只读取需要的行(提速 3 倍)
sales_sample <- read_csv("sales.csv", n_max = 10000)

# 4. 写出干净报表
write_csv(sales_sample, "sales_clean.csv")

1 行代码 = 30 秒 → 5 秒。这就是 readr 的威力。



3. 什么是 CSV?

(1) CSV = Comma-Separated Values

TEXT 📖 仅展示
姓名,年龄,城市
Alice,25,北京
Bob,30,上海
Charlie,35,广州

(2) 为什么数据科学都用 CSV?

优点 说明
通用 Excel、Python、R、数据库都能读
人类可读 记事本打开就能看
体积小 比 Excel 小 5-10 倍
跨平台 不会因 Excel 版本不同乱码
易处理 没有 Excel 的格式/公式噪音
100%
graph LR
    A[CSV 文件<br/>name,age,city] --> B[read_csv 智能读取]
    B --> C[tibble 干净数据框]
    C --> D[write_csv 报表]
    C --> E[dplyr 分析]
    C --> F[ggplot2 可视化]

    style A fill:#cce5ff
    style B fill:#d4edda
    style C fill:#fff3cd
    style D fill:#f8d7da
    style E fill:#e1d4ff
    style F fill:#ffe1d4


4. 基础 R vs tidyverse:两套 CSV 方案

100%
mindmap
    root((CSV 读写的<br/>两套方案))
        基础 R
            read.csv
            write.csv
            慢
            字符串转 factor
        tidyverse
            readr
                read_csv
                write_csv
                read_tsv
            优点
                智能类型推断
                快 5-10x
                大文件进度条
                字符串不转 factor
        选择建议
            新项目: tidyverse
            老代码: read.csv + stringsAsFactors = FALSE
            GB 级数据: data.table::fread

(1) 两套方案对比

特性 基础 R read.csv tidyverse read_csv
速度 慢(基 R 兼容) 快 5-10 倍(C++ 后端)
类型推断 弱(默认 chr) 智能推断(int/dbl/date/lgl)
进度条 ✅(大文件友好)
字符串 默认转 factor 保持字符
返回类型 data.frame tibble(更友好)
列名处理 报错友好 自动清理(空格、特殊字符)
缺失值 NA NA + 自定义字符串
字符串 stringsAsFactors = TRUE 始终 FALSE

(2) 实际对比

R
# 基础 R(老代码常见)
df_old <- read.csv("data.csv")
# 默认行为:所有字符串列转 factor(坑!)

# tidyverse(推荐)
library(readr)
df_new <- read_csv("data.csv")
# 智能推断类型,字符保持字符
💡 提示新项目统一用 readr,老代码遇到 read.csvstringsAsFactors = FALSE

(3) 何时用基础 R?

场景 推荐
新项目 / 写新代码 read_csv (readr)
处理 GB 级超大数据 data.table::fread(更快)
老代码维护 read.csvstringsAsFactors = FALSE
教学 / 简单脚本 两者都行


5. read_csv() 8 个常用参数

(1) 基本语法

R
read_csv(file, col_types = TRUE, col_names = TRUE, na = "NA",
         skip = 0, n_max = Inf, locale = default_locale(),
         progress = TRUE)

(2) 参数速查表

参数 作用 默认值
file 文件路径或 URL 必填
col_types 列类型规范 TRUE(自动推断)
col_names TRUE/向量(自定义列名) TRUE(用第一行)
na 缺失值标记 "NA"
skip 跳过前 N 行 0
n_max 最大读取行数 Inf(全部)
locale 区域设置(编码、小数点) default_locale()
progress 显示进度条 TRUE

(3) 实战:常用参数

R
library(readr)

# 1. 基本读取(智能推断类型)
df <- read_csv("data.csv")

# 2. 指定列类型(避免推断错误)
df <- read_csv("data.csv", col_types = cols(
  id = col_integer(),
  date = col_date(format = "%Y-%m-%d"),
  amount = col_double(),
  name = col_character()
))

# 3. 跳过前 5 行注释
df <- read_csv("data.csv", skip = 5)

# 4. 自定义 NA 标记(CSV 里用 "NULL" 表示缺失)
df <- read_csv("data.csv", na = c("", "NA", "NULL", "N/A"))

# 5. 只读前 1000 行(调试用)
df <- read_csv("data.csv", n_max = 1000)

# 6. 跳过列(用 col_types 设为 col_skip())
df <- read_csv("data.csv", col_types = cols(
  temp_col = col_skip()
))

(4) 智能类型推断详解

readr 的最大优势是自动推断

数据示例 推断为
1, 2, 3 integer
1.5, 2.3 double
2024-01-15 date
12:30:00 time
TRUE, FALSE logical
北京, 上海 character
R
# 推断示例
sales <- read_csv("sales.csv")
spec(sales)  # 查看推断结果
# cols(
#   id = col_integer(),
#   date = col_date(format = "%Y-%m-%d"),
#   product = col_character(),
#   region = col_character(),
#   amount = col_double()
# )


6. write_csv():输出报表

(1) 基本语法

R
write_csv(x, file, na = "NA", append = FALSE, col_names = TRUE)

(2) 实战

R
# 1. 基本输出
write_csv(sales, "sales_clean.csv")

# 2. 输出到指定路径
write_csv(sales, "output/2024_sales.csv")

# 3. 追加到现有文件(不覆盖)
write_csv(new_data, "sales.csv", append = TRUE, col_names = FALSE)

# 4. 自定义 NA 表示
write_csv(df, "output.csv", na = "")

(3) ⚠️ write_csv 不会保留类型

readr 写入 CSV 时,类型信息会丢失(CSV 本身是纯文本)。再读回来要重新推断:

R
# 写出
write_csv(sales, "sales.csv")

# 再读回
sales_loaded <- read_csv("sales.csv")
# 类型由 read_csv 重新推断
💡 提示R 内部持久化用 .rds(保留类型)——见第 12 课。



7. 处理常见问题

(1) 中文编码乱码

R
# 1. 指定编码读取
df <- read_csv("data.csv", locale = locale(encoding = "UTF-8"))
# 或
df <- read_csv("data.csv", locale = locale(encoding = "GBK"))

# 2. 检查文件编码
guess_encoding("data.csv")
# 多数情况返回 "UTF-8" 或 "GBK"

# 3. 写入时指定编码
write_csv(df, "output.csv")  # 默认 UTF-8

(2) 分隔符不是逗号

R
# 1. 制表符分隔(TSV)
df <- read_tsv("data.tsv")  # sep = "\t"

# 2. 分号分隔(欧洲常用)
df <- read_csv2("data.csv")  # sep = ";"

# 3. 自定义分隔符(如 |)
df <- read_delim("data.txt", delim = "|")

(3) 数字带千分位

R
# 欧洲格式:1.234,56(千分位是 .,小数是 ,)
df <- read_csv("data_eu.csv",
               locale = locale(grouping_mark = ".", decimal_mark = ","))

(4) 大文件读取慢

R
# 1. 只读需要的列(提速 2-3 倍)
df <- read_csv("big.csv", col_select = c(id, amount))

# 2. 用 col_types 跳过列
df <- read_csv("big.csv", col_types = cols(
  temp_col_1 = col_skip(),
  temp_col_2 = col_skip()
))

# 3. 关闭进度条
df <- read_csv("big.csv", progress = FALSE)

# 4. 更快的方案:data.table::fread
# fread("big.csv") 比 read_csv 快 5-10 倍


8. 高级:列类型规范 cols()

(1) cols() 完整语法

R
col_types = cols(
  id = col_integer(),       # 整数
  name = col_character(),   # 字符
  amount = col_double(),    # 双精度浮点
  date = col_date(),        # 日期
  time = col_time(),        # 时间
  datetime = col_datetime(),# 日期时间
  is_active = col_logical(),# 逻辑
  skip = col_skip(),        # 跳过该列
  guess = col_guess()       # 让 readr 推断
)

(2) 字符串简写(更简洁)

R
# 用 "i" "c" "d" "D" "l" 简写
col_types = cols(
  id = "i",          # integer
  name = "c",        # character
  amount = "d",      # double
  date = "D",        # date
  is_active = "l"    # logical
)

# 全部推断(默认)
col_types = TRUE

# 用列表(顺序对应列)
col_types = list(
  id = col_integer(),
  name = col_character()
)


9. 完整示例:销售数据导入与分析

下面是一个完整工作流示例,把本课所有知识串起来。

▶ 示例:销售数据导入 + 清洗 + 输出报表

R 📖 仅展示
# ============================================
# 销售数据导入与处理
# 功能:读取 CSV → 清洗 → 分析 → 输出报表
# ============================================

library(readr)
library(dplyr)

# 1. 准备示例数据
sample_data <- tibble(
  id = 1:10,
  date = as.Date("2024-01-01") + 0:9,
  product = c("手机", "电脑", "手机", "平板", "手机",
              "电脑", "平板", "手机", "电脑", "手机"),
  region = c("北京", "上海", "广州", "北京", "深圳",
            "上海", "广州", "北京", "深圳", "上海"),
  amount = c(2999, 5999, 2899, 3999, 3099,
            6299, 3899, 2899, 5999, 3199),
  note = c(NA, "促销", NA, NA, "新品", NA, NA, "促销", NA, NA)
)

# 写入示例 CSV(UTF-8 编码)
write_csv(sample_data, "sales_demo.csv")

cat("=== 文件已生成 ===\n")

# 2. 读取 CSV(智能推断)
sales <- read_csv("sales_demo.csv",
                  na = c("", "NA"),
                  col_types = cols(
                    id = col_integer(),
                    date = col_date(format = "%Y-%m-%d"),
                    product = col_character(),
                    region = col_character(),
                    amount = col_double(),
                    note = col_character()
                  ))

cat("=== 读取结果 ===\n")
print(sales)

# 3. 数据质量检查
cat("\n=== 数据结构 ===\n")
str(sales)

# 4. 数据分析
cat("\n=== 销售统计 ===\n")
summary_stats <- sales |>
  group_by(region) |>
  summarise(
    订单数 = n(),
    总销售 = sum(amount),
    平均订单 = round(mean(amount), 2),
    最高订单 = max(amount)
  ) |>
  arrange(desc(总销售))

print(summary_stats)

# 5. 找出促销订单
cat("\n=== 促销订单 ===\n")
promo <- sales |> filter(!is.na(note))
print(promo)

# 6. 按产品分类
cat("\n=== 产品销售 ===\n")
product_stats <- sales |>
  group_by(product) |>
  summarise(销量 = n(), 销售额 = sum(amount))
print(product_stats)

# 7. 输出干净报表
write_csv(summary_stats, "sales_by_region.csv")
write_csv(product_stats, "sales_by_product.csv")
cat("\n=== 报表已输出 ===\n")
cat("  - sales_by_region.csv(按地区)\n")
cat("  - sales_by_product.csv(按产品)\n")

# 8. 用 RDS 保留类型(推荐)
saveRDS(sales, "sales_clean.rds")
sales_reloaded <- readRDS("sales_clean.rds")
cat("\nRDS 加载后类型:\n")
print(sapply(sales_reloaded, class))
逻辑代码 57 行(超过 40 行限制,仅展示)

预期输出(节选):

TEXT 📖 仅展示
=== 销售统计 ===
# A tibble: 4 × 5
  region 订单数 总销售 平均订单 最高订单
  <chr>   <int>   <dbl>    <dbl>    <dbl>
1 北京       3   9897    3299     3999
2 上海       3  15497    5166.    6299
3 广州       2   6798    3399     3899
4 深圳       2   9098    4549.    5999

=== 促销订单 ===
# A tibble: 2 × 6
     id date       product region amount note
  <int> <date>     <chr>   <chr>   <dbl> <chr>
1     2 2024-01-02 电脑    上海     5999 促销
2     8 2024-01-08 手机    北京     2899 促销

❓ 常见问题

Q 中文 CSV 读取乱码?
Alocale = locale(encoding = "UTF-8")"GBK"。不确定编码先用 guess_encoding() 检查。
Q read_csv 和 fread 怎么选?
A read_csv 是 tidyverse 标配,与 dplyr/ggplot2 整合好;fread 速度更快(GB 级数据首选),但要装 data.table 包。一般项目 read_csv 够用。

📖 小节


📝 作业

  1. 基础题:创建一个包含 5 行 4 列(id/name/score/date)的 tibble,用 write_csv() 写入 test.csv,再 read_csv() 读回,验证类型推断正确(date 列是 Date 类型)。

  2. 基础题:读取上题的 test.csv,用 col_types = cols() 显式指定所有列类型(id 整数、score 双精度、name 字符、date 日期),验证类型与显式指定一致。

  3. 基础题:构造 1 个含 NA 的数据框(用 c(1, NA, 3, NA, 5)),写入 CSV 后用 na = c("NA", "") 读取,验证 NA 正确处理。

  4. 进阶题:模拟 100 行销售数据(product/region/amount/date),用 read_csv 读入后:① 按地区分组求总销售额;② 找出金额最高的 5 个订单;③ 用 write_csv 输出 top5.csvby_region.csv 两个报表。

  5. 挑战题:构造一个含特殊字符的 CSV(列名有空格、字符串含逗号、含 | 分隔符),用 read_csvread_delim 分别读取,对比结果差异。截图保存控制台输出。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏