Как удалить отдельные слова из строк текстового файла?

13

мой текстовый файл выглядит так:

Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

Теперь я хочу удалить Liquid penetration 95% mass (m)из моих строк, чтобы получить только значения. Как я должен это делать?

О.Е.
источник
3
простоgrep -o '[^[:space:]]\+$' file
Авинаш Радж
@AvinashRaj: На данный момент это решение получает «медаль замазки» :)
pa4080
2
@ pa4080 По крайней мере для входных данных, которые я тестировал (10M строк), общий подход Avinash Raj можно сделать на порядок быстрее с помощью PCRE. (Я мог бы подтвердить, что ответственен не шаблон, а механизм, поскольку GNU grep принимает \S+$либо либо, -Eлибо -P.) Так что решение такого рода не является медленным по своей сути. Но я до сих пор не могу получить его где - нибудь близко к αғsнιη по cutметоду , который выиграл свой тест тоже.
Элия ​​Каган,

Ответы:

22

Если есть только один =знак, вы можете удалить все раньше, в том числе =и так:

$ sed -r 's/.* = (.*)/\1/' file
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

Если вы хотите изменить исходный файл, используйте -iопцию после тестирования:

sed -ri 's/.* = (.*)/\1/' file

Примечания

  • -rиспользовать ERE, поэтому мы не должны убегать (и)
  • s/old/newзаменить oldнаnew
  • .* любое количество любых символов
  • (things)сохранить thingsна обратную ссылку позже \1, \2и т.д.
Занна
источник
Спасибо, это сработало. Я использовал эту команду для перезаписи существующего файла: sed -i -r 's /.*= (. *) / \ 1 /' time.txt Можете ли вы объяснить, как это работает?
ОЭ
Почему бы не избежать обратной ссылки? s/^.*= //будет работать одинаково хорошо, так как правильное значение находится в конце строки.
jpaugh
@jpaugh Ну, отчасти потому, что уже слишком поздно менять мой ответ, который был первым опубликованным - другие уже дали решение, которое вы упомянули, и другие более эффективные способы для этого случая :) Но, возможно, показ того, как использовать и \1т.д., имеет некоторую ценность для людей, которые
Занна
@Zanna Это по крайней мере более общее.
jpaugh
21

Это работа для awk; при условии, что значения встречаются только в последнем поле (согласно вашему примеру):

awk '{print $NF}' file.txt
  • NFявляется awkпеременной, расширяется до количества полей в записи (строка), следовательно $NF(обратите внимание на $перед) содержит значение последнего поля.

Пример:

% cat temp.txt 
Liquid penetration 95% mass (m) = 0.000205348
Liquid penetration 95% mass (m) = 0.000265725
Liquid penetration 95% mass (m) = 0.000322823
Liquid penetration 95% mass (m) = 0.000376445
Liquid penetration 95% mass (m) = 0.000425341

% awk '{print $NF}' temp.txt
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
heemayl
источник
13

Я решил сравнить различные решения, перечисленные здесь. Для этого я создал большой файл, основанный на контенте, предоставленном OP:

  1. Я создал простой файл с именем input.file:

    $ cat input.file
    Liquid penetration 95% mass (m) = 0.000205348
    Liquid penetration 95% mass (m) = 0.000265725
    Liquid penetration 95% mass (m) = 0.000322823
    Liquid penetration 95% mass (m) = 0.000376445
    Liquid penetration 95% mass (m) = 0.000425341
    
  2. Затем я выполнил этот цикл:

    for i in {1..100}; do cat input.file | tee -a input.file; done
    
  3. Окно терминала было заблокировано. Я выполнил killall teeиз другого терминала. Затем я проверил содержимое файла с помощью команд: less input.fileи cat input.file. Выглядело хорошо, кроме последней строчки. Поэтому я удалил последнюю строку и создал резервную копию: cp input.file{,.copy}(из-за команд, использующих параметр inplace ).

  4. Окончательное количество строк в файле input.file- 2 192 473 . Я получил этот номер по команде wc:

    $ cat input.file | wc -l
    2192473
    

Вот результат сравнения:

  • grep -o '[^[:space:]]\+$'

    $ time grep -o '[^ [: space:]] \ + $' input.file> output.file
    
    реальный 0m58.539s
    пользователь 0m58.416s
    sys 0m0.108s
    
  • sed -ri 's/.* = (.*)/\1/'

    $ time sed -ri 's /.* = (. *) / \ 1 /' input.file
    
    реальный 0m26.936s
    пользователь 0m22.836s
    sys 0m4.092s
    

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

    $ time sed -r 's /.* = (. *) / \ 1 /' input.file> output.file
    
    реальный 0m19.734s
    пользователь 0m19.672s
    sys 0m0.056s
    
  • gawk '{gsub(".*= ", "");print}'

    $ time gawk '{gsub (". * =", ""); print}' input.file> output.file
    
    реальный 0m5.644s
    пользователь 0m5.568s
    sys 0m0.072s
    
  • rev | cut -d' ' -f1 | rev

    $ time rev input.file | cut -d '' -f1 | rev> output.file
    
    реальный 0m3.703s
    пользователь 0m2.108s
    sys 0m4.916s
    
  • grep -oP '.*= \K.*'

    $ time grep -oP '. * = \ K. *' input.file> output.file
    
    реальный 0m3.328s
    пользователь 0m3.252s
    sys 0m0.072s
    
  • sed 's/.*= //' (соответственно -iопция делает команду в несколько раз медленнее)

    $ time sed 's /.*= //' input.file> output.file
    
    реальный 0m3.310s
    пользователь 0m3.212s
    sys 0m0.092s
    
  • perl -pe 's/.*= //' ( -iопция не дает большой разницы в производительности)

    $ time perl -i.bak -pe 's /.*= //' input.file
    
    реальный 0m3.187s
    пользователь 0m3.128s
    sys 0m0.056s
    
    $ time perl -pe 's /.*= //' input.file> output.file
    
    реальный 0m3.138s
    пользователь 0m3.036s
    sys 0m0.100s
    
  • awk '{print $NF}'

    $ time awk '{print $ NF}' input.file> output.file
    
    реальный 0m1.251s
    пользователь 0m1.164s
    sys 0m0.084s
    
  • cut -c 35-

    $ time cut -c 35- input.file> output.file
    
    реальный 0m0.352s
    пользователь 0m0.284s
    sys 0m0.064s
    
  • cut -d= -f2

    $ time cut -d = -f2 input.file> output.file
    
    реальный 0m0,328s
    пользователь 0m0.260s
    sys 0m0.064s
    

Источник идеи.

pa4080
источник
2
так что мое cut -d= -f2решение побеждает. ха-ха
αғsнιη
Можете ли вы дать больше информации о том, как вы создали этот файл? Кроме того, как wc -lвывести три числа? Когда никакие другие параметры не передаются, эта -lопция должна подавлять все, кроме количества строк.
Элия ​​Каган
@EliahKagan, готово. Я обновил ответ.
pa4080
Ах, я вижу - пробелы были цифровыми разделителями групп. ( wcДействительно ли отображались эти пробелы? Существуют ли языковые настройки, для которых он это сделает?) Спасибо за обновление!
Элия ​​Каган
@EliahKagan: Наконец-то я прочитал ваши вопросы wcеще раз. Я не знаю, где мой ум был сегодня рано, но я действительно не мог их понять. Так что, действительно, пробелы были разделителями групп цифр , и wcне добавляли их :)
pa4080
12

С grepи -Pза то, что PCRE(Интерпретируйте рисунок в виде P erl- C ompatible R Регулярная E Xpression) и -oпечатать только согласованный шаблон. \KУведомит будет игнорировать соответствие часть приходят перед собой.

$ grep -oP '.*= \K.*' infile
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341

Или вы можете использовать cutкоманду вместо этого.

cut -d= -f2 infile
αғsнιη
источник
2
В дополнение к запуску самый быстрый из всех методов , испытанных в тесте pa4080 в , метод в этом ответе также был явным победителем в меньшем тесте я побежал , что испытанные методы , но меньше используется больший входной файл. Это было более чем в десять раз быстрее, чем быстрый вариант метода, который мне лично нравится (и мой ответ в основном о нем). cut
Элия ​​Каган,
11

Поскольку префикс строки всегда имеет одинаковую длину (34 символа), вы можете использовать cut:

cut -c 35- < input.txt > output.txt
Дэвид Фёрстер
источник
6

Обратитесь к содержимому файла с помощью rev, перенаправьте вывод cutс пробелом в качестве разделителя и 1 в качестве целевого поля, затем снова измените его, чтобы получить исходное число:

$ rev your_file | cut -d' ' -f1 | rev
0.000205348
0.000265725
0.000322823
0.000376445
0.000425341
f1nan
источник
5

Это просто, кратко и легко написать, понять и проверить, и мне лично это нравится:

grep -oE '\S+$' file

grepв Ubuntu , когда вызывается с помощью -Eили -P, использует сокращение \s для обозначения пробела (на практике обычно это пробел или табуляция) и \Sдля обозначения всего, что не является единым целым . Используя квантификатор+ и привязку конца строки$ , шаблон \S+$соответствует одному или нескольким непробелам в конце строки . Вы можете использовать -Pвместо -E; смысл в этом случае тот же, но используется другой механизм регулярных выражений , поэтому они могут иметь разные характеристики производительности .

Это эквивалентно прокомментированному решению Avinash Raj (только с более простым и компактным синтаксисом):

grep -o '[^[:space:]]\+$' file

Эти подходы не сработают, если после числа могут стоять пробелы . Они могут быть изменены, чтобы они сделали, но я не вижу смысла вдаваться в это здесь. Хотя иногда поучительно обобщать решение для работы в большем количестве случаев, нецелесообразно делать это почти так часто, как полагают люди, потому что обычно нет никакого способа узнать, каким из множества несовместимых способов в конечном итоге может понадобиться проблема быть обобщенным.


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

$ perl -e 'print((<>) x 2000000)' file > bigfile
$ du -sh bigfile
439M    bigfile
$ wc -l bigfile
10000000 bigfile
$ TIMEFORMAT=%R
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
819.565
$ time grep -oE '\S+$' bigfile > bigfile.out
816.910
$ time grep -oP '\S+$' bigfile > bigfile.out
67.465
$ time cut -d= -f2 bigfile > bigfile.out
3.902
$ time grep -o '[^[:space:]]\+$' bigfile > bigfile.out
815.183
$ time grep -oE '\S+$' bigfile > bigfile.out
824.546
$ time grep -oP '\S+$' bigfile > bigfile.out
68.692
$ time cut -d= -f2 bigfile > bigfile.out
4.135

Я запускал его дважды в случае, если порядок имел значение (как это иногда бывает для задач, связанных с вводом / выводом), и потому, что у меня не было машины, которая не выполняла другие операции в фоновом режиме, которые могли бы исказить результаты. Из этих результатов я делаю следующие выводы, по крайней мере, предварительно и для входных файлов того размера, который я использовал:

  • Вот это да! Передача -P(для использования PCRE ), а не -G(по умолчанию, когда диалект не указан) или -Eвыполняется grepбыстрее на порядок. Поэтому для больших файлов может быть лучше использовать эту команду, чем приведенную выше:

    grep -oP '\S+$' file
  • ВАУ!! cutМетод в ответе αғsнιη в , является более чем на порядок величины быстрее , чем даже более быстрая версия моего пути! Он также стал победителем в тесте PA4080 , который охватывал больше методов, чем этот, но с меньшим вводом - и именно поэтому я выбрал его, из всех других методов, для включения в мой тест. Если производительность важна или файлы огромны, я думаю, что следует использовать метод αғsнιη .cut -d= -f2 filecut

    Это также служит напоминанием о том, что не следует забывать о простых cutи pasteполезных утилитах , и, возможно, их следует отдавать предпочтение, когда это применимо, даже если есть более сложные инструменты, подобные grepэтим, часто предлагаются в качестве решений первой линии (и что я лично более привык к использованию).

Элия ​​Каган
источник
4

perl- s ubstitute шаблона /.*= /с пустой строкой //:

perl -pe 's/.*= //' input.file > output.file
perl -i.bak -pe 's/.*= //' input.file
  • От perl --help:

    -e program        one line of program (several -e's allowed, omit programfile)
    -p                assume loop like -n but print line also, like sed
    -i[extension]     edit <> files in place (makes backup if extension supplied)
    

sed - заменить шаблон пустой строкой:

sed 's/.*= //' input.file > output.file

или (но медленнее, чем указано выше) :

sed -i.bak 's/.*= //' input.file
  • Я упоминаю этот подход, потому что он в несколько раз быстрее, чем в ответе Занны .

gawk- заменить шаблон ".*= "пустой строкой "":

gawk '{gsub(".*= ", "");print}' input.file > output.file
  • От man gawk:

    gsub(r, s [, t]) For each substring matching the regular expression r in the string t,
                     substitute the string s, and return the number of substitutions. 
                     If t is not supplied, use $0...
    
pa4080
источник