Как использовать fread() как readLines() без автоматического обнаружения столбцов?

У меня есть файл размером 5 ГБ. ( > 10 миллионов строк). Формат каждой строки похож на aaaa bb cccc0123 xxx kkkkkkkkkkkkkk или aaaaabbbcccc01234xxxkkkkkkkkkkkkkk, например. Поскольку readLines имеет слабую производительность при чтении большого файла, я выбираю fread(), чтобы прочитать это, но произошла ошибка:

library("data.table")
x <- fread("test.DAT")
Error in fread("test.DAT") : 
  Expecting 5 cols, but line 5 contains text after processing all cols. It is very likely that this is due to one or more fields having embedded sep=' ' and/or (unescaped) '\n' characters within unbalanced unescaped quotes. fread cannot handle such ambiguous cases and those lines may not have been read in as expected. Please read the section on quotes in ?fread.
In addition: Warning message:
In fread("test.DAT") :
  Unable to find 5 lines with expected number of columns (+ middle)

Как использовать fread() как readLines() без автоматического обнаружения столбцов? Или есть ли другой способ решить эту проблему?

Ответ 1

Вот трюк. Вы можете использовать значение sep, которое, как вы знаете, отсутствует в файле. Выполнение этого означает, что fread() читает всю строку как отдельный столбец. Затем мы можем отбросить этот столбец к атомному вектору (показан ниже [[1L]]). Вот пример на csv, где я использую ? как sep. Таким образом, он действует подобно readLines(), только намного быстрее.

f <- fread("Batting.csv", sep= "?", header = FALSE)[[1L]]
head(f)
# [1] "playerID,yearID,stint,teamID,lgID,G,AB,R,H,2B,3B,HR,RBI,SB,CS,BB,SO,IBB,HBP,SH,SF,GIDP"
# [2] "abercda01,1871,1,TRO,NA,1,4,0,0,0,0,0,0,0,0,0,0,,,,,"       
# [3] "addybo01,1871,1,RC1,NA,25,118,30,32,6,0,0,13,8,1,4,0,,,,,"  
# [4] "allisar01,1871,1,CL1,NA,29,137,28,40,4,5,0,19,3,1,2,5,,,,," 
# [5] "allisdo01,1871,1,WS3,NA,27,133,28,44,10,2,2,27,1,1,0,2,,,,,"
# [6] "ansonca01,1871,1,RC1,NA,25,120,29,39,11,3,0,16,6,2,2,1,,,,,"

Другими необычными персонажами, которые вы можете попробовать в sep, являются \ ^ @ # = и другие. Мы видим, что это приведет к тому же выводу, что и readLines(). Это просто вопрос нахождения значения sep, которое отсутствует в файле.

head(readLines("Batting.csv"))
# [1] "playerID,yearID,stint,teamID,lgID,G,AB,R,H,2B,3B,HR,RBI,SB,CS,BB,SO,IBB,HBP,SH,SF,GIDP"
# [2] "abercda01,1871,1,TRO,NA,1,4,0,0,0,0,0,0,0,0,0,0,,,,,"                                  
# [3] "addybo01,1871,1,RC1,NA,25,118,30,32,6,0,0,13,8,1,4,0,,,,,"                             
# [4] "allisar01,1871,1,CL1,NA,29,137,28,40,4,5,0,19,3,1,2,5,,,,,"                            
# [5] "allisdo01,1871,1,WS3,NA,27,133,28,44,10,2,2,27,1,1,0,2,,,,,"                           
# [6] "ansonca01,1871,1,RC1,NA,25,120,29,39,11,3,0,16,6,2,2,1,,,,," 

Примечание.. Как отмечает @Cath в комментариях, вы также можете просто использовать символ разрыва строки \n как значение sep.