• 已编辑

也是没想到一个普通的人畜无害的csv导出功能会这么难用,还挂在tidyverse下面。

具体症状是含中文字符串列,且字符串长度上万时会随机出现串行错字,表现如下:
ref列为实际数据,csv为输出csv后乱码的数据

> xdiff
                      ref                        csv
1            洛菲不动杆菌         疑似致病菌\x8f\x8c
2            细粒棘球绦虫         吡咯素伯克霍尔德菌
3              韦德纤毛菌               呼吸道微生态
4        生信模拟阳性对照                 疑似致病菌
5              头葡萄球菌                 善变副球菌
6            溶血葡萄球菌                 疑似致病菌
7          非发酵棒状杆菌                 疑似致病菌
8              神户肠杆菌                 疑似背景菌
9            流感嗜血杆菌                 疑感嗌景菌
10           蒂莫内马赛菌               疑似臅马赛菌
11       甜瓜鞘氨醇单胞菌                 绳状蓝状菌
12           解糖葡萄球菌                 疑似致病菌
13           解糖普雷沃菌             厌氧消化链球菌
14           真口普雷沃菌                 疑似致病菌
15           懒惰约翰森菌             懒惰纴桑纤毛菌
16         特雷维桑纤毛菌             懒惰纴桑纤毛菌
17                酸杆菌*                  厌氧球菌*
18           约氏不动杆菌                 疑似背景菌
19   克罗彭施泰特棒状杆菌               颗粒丙酸杆菌
20           产酸丙酸杆菌         疑似致病菌\x8f\x8c

一个可重复例子

##下载数据:  https://thunderbird.visionmedicals.cn/tmp_storage/testdata.rda

load("testdata.rda")
readr::write_excel_csv(testdata,file = "~/testx2.csv")

testdata_csv= data.table::fread("~/testx2.csv")

xdiff = data.frame(
	ref = testdata$species_chinese[which(testdata_csv$species_chinese != testdata$species_chinese)],
	csv = testdata_csv$species_chinese[which(testdata_csv$species_chinese != testdata$species_chinese)]
)

xdiff

目前我的解决办法是替换为data.table::fwrite()

R version 4.3.2 (2023-10-31)
Platform: x86_64-conda-linux-gnu (64-bit)
Running under: Ubuntu 20.04.5 LTS

Matrix products: default
BLAS/LAPACK: /home/tchzhang/miniconda3/envs/renv423/lib/libopenblasp-r0.3.21.so;  LAPACK version 3.9.0

locale:
 [1] LC_CTYPE=C.UTF-8           LC_NUMERIC=C              
 [3] LC_TIME=zh_CN.UTF-8        LC_COLLATE=C.UTF-8        
 [5] LC_MONETARY=zh_CN.UTF-8    LC_MESSAGES=C.UTF-8       
 [7] LC_PAPER=zh_CN.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=zh_CN.UTF-8 LC_IDENTIFICATION=C       

time zone: Asia/Shanghai
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_4.3.2
    load("testdata.rda")
    readr::write_excel_csv(testdata,file = "~/testx2.csv")
    
    testdata_csv= data.table::fread("~/testx2.csv")
    
    xdiff = data.frame(
      ref = testdata$species_chinese[which(testdata_csv$species_chinese != testdata$species_chinese)],
      csv = testdata_csv$species_chinese[which(testdata_csv$species_chinese != testdata$species_chinese)]
    )
    xdiff
    #> [1] ref csv
    #> <0 rows> (or 0-length row.names)

    <sup>Created on 2025-01-14 with reprex v2.1.1</sup>

    数据被处理过了?没法重现呢……不过看样子我总感觉这当中有文件编码的问题……

      fenguoerbian

      随机的,每次跑 乱的形式都不一样

      tctcab 可能是字符编码的问题。数据中的中文字符列都没有显式的编码,如:

      > table(Encoding(testdata$species_chinese))
      
      unknown 
        19998 

      编码为 unknown 的字符在不同的系统区域语言设置(locale)下可能会有不同的解释。为了避免这种模糊性,最好是在数据的源头读取和终点写入这两个步骤里都显式声明编码(例如 read.csvwrite.csvfileEncoding 参数)。我看了一下 readr 文档,read_csv 没提它默认的文件编码是什么,也没有参数可以控制;write_csv 说它写文件时用的是 UTF-8。可能就是你这个 unknown 编码转 UTF-8 的过程中出了问题。不过我在 macOS 下循环运行了多次也没能重现你的问题;要重现的话至少还得需要你的 sessionInfo()。你可以看看显式转换编码到 UTF-8 之后问题是否还可以重现:

      testdata2 = as.data.frame(lapply(testdata, function(x) {
        if (is.character(x)) x = enc2utf8(x) else x
      }))

        楼上正解,大概就是数据在流转中产生了编码问题,更多可能是 base R + Windows 的组合导致的…… 比如 tidyverse/readr#697 汇报的。

        试了一下 Python,在 Windows 下的输出没有重现楼主的问题:

        from pyreadr import read_r
        
        x = read_r("testdata.rda")
        df = x["testdata"]
        df.to_csv("out.csv", index=False)

        测试系统的 locale 是

        Sys.getlocale()
        #> [1] "LC_COLLATE=English_United States.utf8;LC_CTYPE=English_United States.utf8;LC_MONETARY=English_United States.utf8;LC_NUMERIC=C;LC_TIME=English_United States.utf8"

        yihui

        赞,gpt搞不定的东西还是得找老水手 😆