library(data.table)
setDT(test)[, tstrsplit(x, "-", type.convert = TRUE, fixed = TRUE)]
# V1 V2 V3 V4
# 1: NA NA NA NA
# 2: 1 7 9 NA
# 3: 3 NA NA NA
# 4: 2 4 6 8
Note: data.table dev version 1.9.5. The type.convert argument and factor to character conversion have been implemented in the latest dev version per #1094 (Thanks Arun!).
Or
splitstackshape::cSplit(test, "x", "-")
# x_1 x_2 x_3 x_4
# 1: NA NA NA NA
# 2: 1 7 9 NA
# 3: 3 NA NA NA
# 4: 2 4 6 8
These both return data tables that can be converted back to data frames by assigning the result then using setDF(). They also both properly convert the numeric characters to classed "integer" columns.
And just for fun, a really difficult way to get a data frame back with scan()
x <- as.character(test$x)
v <- max(vapply(strsplit(x, "-", fixed = TRUE), length, 1L))
s <- scan(text = x, what = as.list(integer(v)), sep = "-", fill = TRUE,
na.strings = "", blank.lines.skip = FALSE)
setNames(data.frame(s), make.names(seq_along(s)))
# X1 X2 X3 X4
# 1 NA NA NA NA
# 2 1 7 9 NA
# 3 3 NA NA NA
# 4 2 4 6 8