Заменить определенные пробелы на вкладки - разделители

У меня есть один столбец data.frame, где некоторые пробелы должны быть ограничителями некоторого пробела.

#input data
dat <- data.frame(x=c("A 2 2 textA1 textA2 Z1",
                      "B 4 1 textX1 textX2 textX3 Z2",
                      "C 3 5 textA1 Z3"))
#                               x
# 1        A 2 2 textA1 textA2 Z1
# 2 B 4 1 textX1 textX2 textX3 Z2
# 3               C 3 5 textA1 Z3

Необходимо преобразовать его в 5 столбцов data.frame:

#expected output
output <- read.table(text="
A   2   2   textA1 textA2   Z1
B   4   1   textX1 textX2 textX3    Z2
C   3   5   textA1  Z3",sep="\t")
#   V1 V2 V3                   V4 V5
# 1  A  2  2        textA1 textA2 Z1
# 2  B  4  1 textX1 textX2 textX3 Z2
# 3  C  3  5               textA1 Z3

По существу, необходимо изменить 1-е, 2-е, 3-е и последнее пространство на вкладку (или любой другой разделитель, если это упростит код).

Игра с regex еще не дает ничего полезного...

Примечание1: В реальных данных мне нужно заменить 1, 2, 3,..., 19 и последние пробелы на вкладки.
Примечание2: В V4 нет шаблона, текст может быть любым.
Примечание3: Последний столбец - это текст из одного слова с переменной длиной.

Ответ 1

Try

v1 <- gsub("^([^ ]+)\\s+([^ ]+)\\s+([^ ]+)\\s+", '\\1,\\2,\\3,', dat$x)
read.table(text=sub(' +(?=[^ ]+$)', ',', v1, perl=TRUE), sep=",")
#  V1 V2 V3                   V4 V5
#1  A  2  2        textA1 textA2 Z1
#2  B  4  1 textX1 textX2 textX3 Z2
#3  C  3  5               textA1 Z3

Или вариант, вдохновленный сообщением @Tensibai

n <- 3
fpat <- function(n){
   paste0('^((?:\\w+ ){', n,'})([\\w ]+)\\s+(\\w+)$')
}

read.table(text=gsub(fpat(n), "\\1'\\2' \\3", dat$x, perl=TRUE))
#  V1 V2 V3                   V4 V5
#1  A  2  2        textA1 textA2 Z1
#2  B  4  1 textX1 textX2 textX3 Z2
#3  C  3  5               textA1 Z3

Для большего количества столбцов

 n <- 19
 v1 <- "A 24 34343 212 zea4 2323 12343 111 dsds 134d 153xd 153xe 153de 153dd dd dees eese tees3 zee2 2353 23335 23353 ddfe 3133"

 read.table(text=gsub(fpat(n), "\\1'\\2' \\3", v1, perl=TRUE), sep='')
 # V1 V2    V3  V4   V5   V6    V7  V8   V9  V10   V11   V12   V13   V14 V15
 #1  A 24 34343 212 zea4 2323 12343 111 dsds 134d 153xd 153xe 153de 153dd  dd
 #  V16  V17   V18  V19                   V20  V21
 #1 dees eese tees3 zee2 2353 23335 23353 ddfe 3133

Ответ 2

С переменным числом столбцов:

library(stringr)
cols <- 3
m <- str_match(dat$x, paste0("((?:\\w+ ){" , cols , "})([\\w ]+) (\\w+)"))
t <- paste0(gsub(" ", "\t", m[,2]), m[,3], "\t", m[,4])

> read.table(text=t,sep="\t")
  V1 V2 V3                   V4 V5
1  A  2  2        textA1 textA2 Z1
2  B  4  1 textX1 textX2 textX3 Z2
3  C  3  5               textA1 Z3

Измените количество столбцов, чтобы указать, сколько вы пожелаете раньше. Для регулярного выражения:

  • ((?:\\w+ ){3}) Захватите 3 повторения {3} группы, не захватывающей (?:\w+ ), которая содержит хотя бы один буквенно-цифровой символ w+, за которым следует пробел
  • ([\\w ]+) (\w+) захватить свободный текст из буквенно-цифрового char или пробела [\w ]+, а затем пробел и захватить последнее слово с помощью \w+

После этого вставьте 3 части, возвращенные str_match, позаботившись о замене пробелов в первой группе m[,2] вкладками.

m[,1] - это полный матч, поэтому он не используется здесь.


Старый ответ:

Базовое совпадение на основе фиксированного числа полей:

> read.table(text=gsub("(\\w+) (\\w+) (\\w+) ([\\w ]+) (\\w+)$","\\1\t\\2\t\\3\t\\4\t\\5",dat$x,perl=TRUE),sep="\t")
  V1 V2 V3                   V4 V5
1  A  2  2        textA1 textA2 Z1
2  B  4  1 textX1 textX2 textX3 Z2
3  C  3  5               textA1 Z3

Добавьте столько (\ w +), которое вы пожелаете раньше, и увеличьте число \1 (обратные ссылки)

Ответ 3

Здесь может быть один поворотный способ, который будет работать независимо от количества "слов", которые у вас есть (и это работает с вашими данными); это основано на количестве символов алфавита в ваших "словах" по сравнению с количеством символов алфавита в других полях:

res <- gsub("\\w{3,}\\K\\t(?=\\w{3,})", " ", gsub(" ", "\t", dat$x), perl=T)
res
# [1] "A\t2\t2\ttextA1 textA2\tZ1"        "B\t4\t1\ttextX1 textX2 textX3\tZ2" "C\t3\t5\ttextA1\tZ3"

read.table(text=res, sep="\t")
#  V1 V2 V3                   V4 V5
#1  A  2  2        textA1 textA2 Z1
#2  B  4  1 textX1 textX2 textX3 Z2
#3  C  3  5               textA1 Z3

РЕДАКТИРОВАТЬ: совершенно другой способ пойти, только исходя из количества пробелов k, которые необходимо заменить перед последним:

k <- 3 # in your example
res <- sapply(as.character(dat$x), 
              function(x, k){
                 pos_sp <- gregexpr(" ", x)[[1]]
                 x <- strsplit(x, "")[[1]]
                 if (length(pos_sp) > k+1) pos_sp <- pos_sp[c(1:k, length(pos_sp))]
                 x[pos_sp] <- "\t"
                 x <- paste(x, collapse="")
               }, k=k)

read.table(text=res, sep="\t")
#   V1 V2 V3                   V4 V5
# 1  A  2  2        textA1 textA2 Z1
# 2  B  4  1 textX1 textX2 textX3 Z2
# 3  C  3  5               textA1 Z3