本楼主今天想把一些 sql 文件里面的脚本给拼接到一个文件里面,方便查询。使用的代码如下:

library(data.table)

a <- list.files(path = "D:/目标目录/", pattern = "*.sql")

dir <- paste("D:/目标目录/", a, sep = "")

n <- length(a)

merge.data <- fread(
  file = dir[1],
  header = FALSE,
  sep = "\t",
  fill = TRUE,
  # 设置不去掉每行首尾空格
  strip.white = FALSE
)

for (i in 2:n) {
  new.data = fread(
    file = dir[i],
    header = FALSE,
    sep = "\t",
    fill = TRUE,
    strip.white = FALSE
  )
  merge.data = rbind(merge.data, new.data, fill = TRUE)
}

fwrite(merge.data,
       file = "D:/目标目录/文件名称.sql",
       row.names = F,
       # 当quote="auto"时,若字段内容中包含分隔符、以\n结尾、单双引号等导出时整行字段会用双引号括起来
       quote = FALSE) 

现在遇到的报错是:

Error in fread(file = dir[i], header = FALSE, sep = "\t", fill = TRUE,  : 
  单列输入包含了不合法的引用。自我修正只有在列数大于1(ncol>1)时才有效

前面遇到的有些问题,我还能看懂报的撒错,可以去翻翻文档来解决,现在报的这个错我看不懂,上网也没搜出来类似的错误。另外,也还没想到办法,怎么定位问题所在。发个帖子,看看坛子里的小伙伴们有没有人遇见过类似情况的?

    出错时候i是多少应该能帮你定位到是哪个文件,然后试着去删删看拿个文件里一些行,如果之后fread能正常了,那大概就能定位到是哪一行,或者哪个字符出问题了?

    另外让我不解的是rbind也有fill参数吗?它会不会给你bind一个叫做fill的元素,内容是TRUE?

    再另外向这种要合并的操作可以考虑do.call,这样不用整个循环分成两部分来写。

    res_list <- list()
    for(i in 1 : n){
        res_list[[i]] <- fread()
    }
    res <- do.call(rbind, res_list)

      是想把某个文件夹下面的所有脚本代码(文本文件)读取在一块吗,如果是的话我一般会这么写

      lapply(
        dir('your/file/path', pattern = '\\.R$', full.names = T),
        readLines
      )

      读取进来会是一个list,每一个元素对应一个脚本的所有文本,一个长度为文本行数的字符向量,之后无论是想用字符处理直接查找对应代码,还是do.call(c, ...)之类的合并之后用writeLines输出到别的文件都可以

        sql脚本是纯文本文件吗?是的话用 bash 就行。

        cat *.sql > merged-sql-files

          meeeeeeeeo

          我试了试喵君你的方法,用 dir 函数替代 list.files 函数,并且用 readLines 替代 fread 来读入脚本,倒是能够正常读入,也能正常导出。可是问题是,不管是用 writeLines 导出,还是用分果而半教的 do.call 以后再用 fwrite 导出,原来脚本里面的格式都没有了。这是 readLines 的特点。就是格式消失以后,再用眼睛看,强迫症又犯了……脚本如下:

          data <- lapply(dir(
            path = '目标目录/',
            pattern = '\\.sql$',
            full.names = TRUE,
            ignore.case = TRUE
          ),
          readLines)
          
          n <- length(data)
          
          res_list <- list()
          for (i in 1:n) {
            res_list[[i]] <- data[[i]]
          }
          res <- do.call(rbind, res_list)
          
          #writeLines(data[[1]], con = "目标目录/tmp.sql", sep = "\t")
          
          fwrite(res,
                 file = "目标目录/tmp.sql",
                 row.names = F,
                 quote = FALSE) 

            CyrusYip
            我是在本地电脑上鼓捣这个事,系统是 windows,暂时没找到可以输入 bash 脚本的入口,不造那个 git-bash 可不可以拿来用?

            补充一下,用 git-bash 可以,cd 到目标目录下面,执行叶寻同学的脚本就可以了。但是问题是,如果一个文件夹下面套着多个文件夹的话,只会把该文件夹下面的.sql文件读取出来,不会把多个文件夹下面的文件都读出来。我去瞅瞅 cat 具体咋用的先。

              fenguoerbian

              另外让我不解的是rbind也有fill参数吗?它会不会给你bind一个叫做fill的元素,内容是TRUE?

              加这个是因为执行的时候,报了一个错,大意是列数不相等没法拼到一起,提示加上fill = TRUE。我刚才?rbind看了一下,在 base 的 rbind 函数里面确实没有 fill 参数,但是在 data.table 的 rbindlist 和 rbind 有这个参数,约莫是我执行脚本的时候是 data.table 包给我提示的错误。

              我又复现了一下那个错误,长这样:

              Error in rbindlist(l, use.names, fill, idcol) : 
                项2有6列, 与第1项不一致, 它有1列. 若要填补这些列, 请使用fill=TRUE.

              有趣的是,我把一楼merge.data = rbind(merge.data, new.data, fill = TRUE)这里的rbind改成rbindlist的话,又报下面的错:

              Error in rbindlist(merge.data, new.data, fill = TRUE) : 
                输入为 data.table 应该叠加普通列表项

              感觉我原来用 rbind 加上fill = TRUE像是误打误撞领了一块免死金牌。

              快刹车快刹车,已经跑得有点歪了。这个任务本来应该是很简单的。

              yuanfan 原来脚本里面的格式都没有了

              如果只是拼接一系列 .sql 文件,那么不需要 rbind(),只需要把你那个大列表打散即可(unlist(data))。

              我在 xfun 包中有个 read_all() 函数,就是干这个的。

              files <- list.files( '目标目录/', '\\.sql$', full.names = TRUE, ignore.case = TRUE)
              text <- xfun::read_all(files)
              xfun::write_utf8(text, '目标目录/tmp.sql')

              若不用 xfun,那么如此这般写:

              files <- list.files( '目标目录/', '\\.sql$', full.names = TRUE, ignore.case = TRUE)
              text <- unlist(lapply(files, readLines))
              writeLines(text, '目标目录/tmp.sql')

              yuanfan 原来脚本里面的格式都没有了

              好奇你说的格式指的什么,如果是\n \t空格之类的缩进换行,readLines+writeLines都能正确的读入导出的,上面Yihui写的代码你应该能直接拿来跑

                yuanfan 我没用过 git-bash 呢。原来你有多个文件夹。那就这样:

                shopt -s globstar # 开启 globstar
                cat **/*.sql > merged-sql-files

                我不知道 git-bash 的文件路径是用 / 还是 \

                **(globstar)表示任意文件夹,*表示任意字符,cat 就是输出文件内容,> 把输出写入文件。

                Bash 4.0 开始才支持 globstar,用 bash --version 可以检查版本。

                lovebluesky
                我搜了一下 WSL,据说是适用于 windows 的 Linux 子系统。前两天我公司同事刚建议我自己装一次虚拟机,用来了解下部署方面的知识,不造这个 WSL 是不是就是。

                话又说回来,其实我一看到 WSL ,首先想到的是“猥琐流”……

                meeeeeeeeo
                格式就是指代码格式化的那个格式,就是你说的空格的缩进换行。益辉大神给的代码我试过能跑出来。

                嗷,本新手的 R 太菜了,此帖子的知识点回头我抽空肝一篇博客来整理一下。

                  yuanfan 了解Linux装虚拟机是对的。WSL就是在Windows运行Linux程序的技术。WSL1没用Linux内核,但可以执行Linux程序,简直是魔法,有点 Wine 的感觉。WSL2是把Linux运行在虚拟机。

                  Windows subsystem for Linux 这个名字我每次看了都皱眉头,十分不解。明明是 Linux for Windows (for表示供……使用),叫 Linux inside Windows 也好,怎么会 for Linux 。

                  讲一个冷知识,微软曾经有个技术(Project Astoria)可以让windows 10 mobile 运行安卓程序,只在win10 mobile 预览版出现过。WSL就是从那个技术衍生的。

                  yuanfan 比虚拟机占资源少多了,图形界面好像需要折腾,如果用命令行比较方便

                  yuanfan 如果要线上部署模型,不如玩 docker 也是弄一个虚拟环境,而且跨平台,易扩展。后续可玩的空间非常大!

                  9 天 后

                  yuanfan
                  啊,我写了一篇博客,整理了本帖里面提到的方法https://yuanfan.rbind.io/project/r-paste/

                  写的时候突然想到 R 里面是不是也会有 Linux 的 cat,于是乎多写了一个方法四,即用 scan 和 cat 函数来读取和导出。但是这个方法有一个细节不够完善,就是用 cat 函数导出的时候,如果只设定fill = TRUE的话,有些本应多行的会连成一行,但是再加上sep = '\n'的话,不会出现多行连成一行的情况,可是同时会导致本来正常换行的行之间多出一行。

                  举个栗子。正常应该导出下面这样的。

                  select a.col1,
                  b.col2
                  from table1 a
                  join table2 b on a.id=b.id

                  如果 cat 函数里设置fill = TRUE,会出现下面这种多行合为一行的。

                  select a.col1, b.col2
                  from table1 a 
                  join table2 b on a.id=b.id

                  如果再加上sep = '\n',又会变成下面这样。

                  select a.col1,
                  b.col2
                  from table1 a
                  
                  join table2 b on a.id=b.id

                    yuanfan 上面我喊快刹车的意思是,这个任务引出了一些完全跑偏了的工具,诸如 read.table / write.table / fread / fwrite / scan / rbind / rbindlist 都是高射炮打蚊子,而且高射炮的准星还是歪的。这个任务就是简单地读取文本、拼接、再写出去。只是普通文本而已,并非结构化的数据,即:每一行并非带分隔符的一行表格数据,所以不能硬套 read.table 之类的工具。唯一瞄准楼主任务的就是 CyrusYip 说的命令行(cat >cat 完成读取和拼接,> 完成写)。如果不用命令行工具,那么就是我说的,readLines() + writeLines() 完事。要是硬按表格数据去读写,那真的是“日滴跑四方,夜里补裤裆”,会有很多裤裆要补的;比如 fill = TRUEsep = '\n' 就是在补,而且还很难补好。为什么难补?因为出手就错了,后面要拆了口袋补裤脚、剪了裤腿缝裤腰。此起彼伏的洞。

                    当然,我完全同意 @Cloud2016折腾不会白了少年头。这样折腾会涨很多经验值,也会更快成为更好的裁缝。