2011-01-07 27 views
11

Wyobraź masz plik .txt o następującej strukturze:Algorytm tekstu filtrowania plików

>>> header 
>>> header 
>>> header 
K L M 
200 0.1 1 
201 0.8 1 
202 0.01 3 
... 
800 0.4 2 
>>> end of file 
50 0.1 1 
75 0.78 5 
... 

chciałbym odczytać wszystkie dane z wyjątkiem linii oznaczonych >>> i linii poniżej linii >>> end of file. Do tej pory rozwiązałem to, używając read.table(comment.char = ">", skip = x, nrow = y) (x i y są obecnie naprawione). To odczytuje dane między nagłówkiem a >>> end of file.

Chciałbym jednak, aby moja funkcja była trochę bardziej plastyczna pod względem liczby rzędów. Dane mogą mieć wartości większe niż 800, a w konsekwencji więcej wierszy.

Mogę scan lub readLines plik i zobaczyć, który wiersz odpowiada >>> end of file i obliczyć liczbę linii do odczytania. Jakie podejście zastosowałbyś?

+0

Proszę podać fałszywe dane. =) – aL3xa

+0

@ aL3xa: czy pokazany fragment jest niewystarczający? –

Odpowiedz

11

Oto jeden ze sposobów, aby to zrobić:

Lines <- readLines("foo.txt") 
markers <- grepl(">", Lines) 
want <- rle(markers)$lengths[1:2] 
want <- seq.int(want[1] + 1, sum(want), by = 1) 
read.table(textConnection(Lines[want]), sep = " ", header = TRUE) 

co daje:

> read.table(textConnection(Lines[want]), sep = " ", header = TRUE) 
    K L M 
1 200 0.10 1 
2 201 0.80 1 
3 202 0.01 3 
4 800 0.40 2 

na danych urywek podać (w pliku foo.txt, a po usunięciu ... Linie).

+1

+1, miło jest poznać "rle", którego wcześniej nie używałem. Zastanawiam się jednak, czy istnieje sposób modyfikowania definicji 'read.table' (i/lub' scan' i/lub 'readLines'), poprzez dodanie opcjonalnego argumentu' EOF' tak, aby był on przenoszony, gdy napotka Ciąg 'EOF'. W ten sposób moglibyśmy zrobić to w jednym przebiegu zamiast 2. –

+0

Argument EOF byłby miłym dodatkiem. –

+0

Miałem nadzieję, że istnieje sposób dodania opcjonalnego arg EOF do definicji źródła dla 'scan', ale wywołuje' .Internal (scan ...) ', więc jedynym sposobem jest zmiana wewnętrznego (C?) kod do skanowania ... –

11

Oto kilka sposobów.

1) readLine czyta w liniach pliku do L i ustawia skip do liczby linii, aby przejść na początek i end.of.file do liczby linii oznakowania koniec danych rzędu. Komenda read.table wykorzystuje następnie te dwie zmienne do ponownego odczytania danych.

File <- "foo.txt" 

L <- readLines(File) 
skip <- grep("^.{0,2}[^>]", L)[1] - 1 
end.of.file <- grep("^>>> end of file", L) 

read.table(File, header = TRUE, skip = skip, nrow = end.of.file - skip - 2) 

Odmiana byłoby użyć textConnection zamiast File w linii read.table:

read.table(textConnection(L), header = TRUE, 
    skip = skip, nrow = end.of.file - skip - 2) 

2) Inną możliwością jest użycie sed lub awk/gawk. Rozważmy ten jeden program gawk linii. Program kończy działanie, jeśli widzi linię oznaczającą koniec danych; w przeciwnym razie pominie bieżącą linię, jeśli linia zaczyna się >>> i jeśli żadna z nich się nie stanie, drukuje linię. Możemy przetasować foo.txt poprzez program gawk i odczytać go przy pomocy read.table.

cat("/^>>> end of file/ { exit }; /^>>>/ { next }; 1\n", file = "foo.awk") 
read.table(pipe('gawk -f foo.awk foo.txt'), header = TRUE) 

Odmianą tego jest to, że możemy pominąć część /^>>>/ {next}; programu gawk, która pomija się >>> linii na początku i używać comment = ">" in the read.table` zamiast zadzwonić.

+0

+1 miłe kilka odpowiedzi Gabor, zwłaszcza awk jeden. –

+0

Rozwiązanie awk/gawk byłoby bardzo przydatne, gdybyś nie mógł wcześniej przewidzieć struktury pliku. –