2012-02-05 2 views
2

Интересно, есть ли эффективный способ удалить первую строку в файле, если она соответствует указанному шаблону. Например, у меня есть файл с данными следующего вида:Удалить первую строку в файле, если она соответствует шаблону

Date,Open,High,Low,Close,Volume,Adj.Volume 
2012-01-27,42.38,42.95,42.27,42.68,2428000,42.68 
2012-01-26,44.27,44.85,42.48,42.66,5785700,42.66 
. 
. 
. 

Я хочу удалить первую строку, только если она содержит текст (как показано в примере в первой строке), и оставить его без изменений, если он содержит только числа (как и в остальных строках). Эта задача довольно легко и я достиг ее, применяя следующий мир кода, который записывает каждую строку в $newFile тех пор, пока она не включает в Date схемы:

while(<$origFile>) 
    { 
     chomp($_); 
     print $newFile $_ unless ($_ =~ m/Date/g) 
    } 

Так как я уже говорил, что делает Работа выполнена. Однако кажется, что очень большая трата ресурсов для чтения каждой строки в целом файле, когда известно, что текст может отображаться только в первой строке.

Есть ли способ более эффективно выполнить эту задачу?

ПРИМЕЧАНИЕ: Я уже нашел почти аналогичный вопрос here, но так как я хочу, чтобы мой код также был доступен для Linux и Windows, использование sed не поможет мне здесь.

Заранее благодарен!

+4

Ваш код будет удалить эту строку текста, независимо от положения в файле. Что касается неэффективности: вы должны прочитать все строки в файле, вы не можете удалить байты с начала файла (так работают файловые системы). Примечание: вы найдете свой ответ в [Perl: Как удалить первую строку файла без чтения и копирования всего файла] (http://stackoverflow.com/q/3016734/112968) - просто объединитесь с вашим регулярным выражением. – knittl

+1

(1) Вы не хотите, чтобы chomping не добавлял обратно новую строку, потому что это поместит весь ваш файл ввода на одну строку! (2) Вам не нужно указывать «$ _ = ~», потому что m // работает по $ _ по умолчанию. (3) Вам не нужен флаг «g» на m //; здесь ничего не делается. – zgpmax

ответ

3

$. можно использовать для определения, обрабатывают ли семью строку.

perl -i.bak -ne'print if $. != 1 || !/^Date/;' file 
Однако, кажется, что это большая трата ресурсов читать каждую строку в целом файле

Это невозможно удалить из любой точки мира, но конец файла. Чтобы удалить с самого начала или в середине, все, что следует в файле, нужно сдвинуть, что означает, что он должен быть как прочитан, так и написан.

Вы можете избежать работы только в том случае, если первая строка не соответствует (ничего не делая). Если вам нужно удалить строку, вы должны скопировать весь файл.

2

Модуль Tie::File идеально подходит для этого. Он очень эффективен, поскольку он блокирует IO вместо чтения строки за раз, и это делает программу очень простой для записи.

use strict; 
use warnings; 

use Tie::File; 

tie my @data, 'Tie::File', 'mydatafile' or die $!; 
shift @data if $data[0] =~ /Date/; 
untie @data; 
1

только сделать тест на первой линии, а затем просто запустить через остальную часть файла без проверки:

if (defined($_ = <$origFile>)) { 
    if (! m/Date/o) { print $newFile $_ } 

    my $data; 

    for (;;) { 
     my $readRes = read($origFile, $data, 0x10000); 

     if (!defined $readRes) { die "Can't read: $!" } 

     if ($readRes == 0) { last } 

     print $newFile $data; 
    } 
} 
+0

Привет и спасибо за ваш ответ! Однако я не уверен, что полностью понимаю ваш код ... Не могли бы вы дать какое-то объяснение, в частности параметры 'for (;;)' и 'read'. Спасибо! –

+1

'for (;;)' является сокращением для 'while (1)', т. Е.цикл без указания условия выхода (мы выходим из цикла в середине, используя 'last'). Встроенная функция 'read' документируется по адресу http://perldoc.perl.org/functions/read.html – zgpmax

Смежные вопросы