Який хороший спосіб відфільтрувати текстовий файл, щоб видалити порожні рядки?


11

У мене є .csv файл (на mac), який містить купу порожніх рядків, наприклад:

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum 

lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum 

lorem ipsum ","2","3","4"

Який я хочу перетворити на:

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum  lorem ipsum ","2","3","4"

Я знаю, що повинен бути один лайнер, але я не знаю ні awk, ні sed. Будь-які поради дуже вдячні!


1
Відповідно до цього зразка ви фактично хочете видалити вбудовані розриви рядків з полів. Це правильно? Іншими словами, є 6 вхідних рядків і повинні бути 2 вихідні лінії?
манатурка

Так, саме цього я намагаюся позбутися: вбудовані нові рядки всередині цитованого рядка.
pitosalas

Отже, вам потрібно щось, що видаляє нові рядки з лапок. Це буде трохи складніше, тому що вам потрібен багаторядковий регулярний вираз.
tongpu

Відповіді:


11

Ви можете використовувати режим grep -v(обернена відповідність) для цього:

grep -v '^$' old-file.csv > new-file.csv

Зауважте, що вони повинні бути різними файлами через те, як працює переадресація оболонки. Вихідний файл відкривається (і спорожняється) перед тим, як прочитати вхідний файл. Якщо у вас є додаткові утиліти (не за замовчуванням у Mac OS X), ви можете використовувати spongeдля цього:

grep -v '^$' file.csv | sponge file.csv

Але звичайно, тоді вам важче повертатись назад, якщо щось піде не так.

Якщо "порожні рядки" насправді можуть містити пробіли (це здається, що вони є), ви можете використовувати це замість цього:

egrep -v '^[[:space:]]*$' old-file.csv > new-file.csv

Це ігнорує порожні рядки, а також рядки, що містять лише пробіли. Звичайно, ви можете зробити те саме spongeперетворення на ньому.


Дякую .... Не видалили порожні рядки ... Можливо, ^ $ не відповідає? Але рядки порожні, наскільки мені відомо. Пам'ятайте, це компакт-диск, створений програмою excel на mac ... Це щось говорить? (Не біжіть кричати, бо я сказав Excel :)
pitosalas

@pitosalas Вони, мабуть, не порожні рядки. Спробуйте змінити його на egrep -v '^[[:space:]]*$'... note grep -> egrep та новий дивний візерунок
derobert

Не працював.
Видалили

@pitosalas Я не знаю, як би видалити подвійні лапки. Він повинен мати змогу видаляти лише пробіли. І справді, це робиться, коли я перевіряю це на прикладі даних, які ви опублікували ...
derobert

@pitosalas ви могли б перевірити, чи якась із цих команд випльовує щось, що виглядає розумним (на відміну від хитрості): iconv -f utf16le file.csv | headабоiconv -f utf16be file.csv | head
derobert

8

Найпростіший варіант - просто grep .. Тут крапка означає "відповідати будь-чому", тому якщо рядок порожній, він не збігається. Інакше він друкує всю лінію як є.


6

Щоб видалити порожні рядки, замість ksh93:

sed '/./!d' file 1<>; file

Оператор <>;перенаправлення характерний для ksh93 і такий же, як і стандартний <>оператор, за винятком того, що ksh урізає файл після завершення команди.

sed '/./!d'це складний спосіб запису grep ., але, на жаль, GNU grep принаймні скаржиться, якщо його stdout вказує на той самий файл, що і його stdin. Ви б сказали, що можна написати:

grep . file | cat 1<>; file

Але, на жаль, у ksh93 є помилка (принаймні, моя версія (93u +)), оскільки файл, здається, у цьому випадку врізаний до нульової довжини.

grep . file | { cat; } 1<>; file

Здається, що обходиш цю помилку, але зараз вона набагато складніше, ніж команда sed.


Об’єднайте свої відповіді в один добре відформатований запис із коротким керівництвом щодо використання кожного рішення. Різні підходи до різних проблем, зібрані разом у плаваючі відповіді, зробили це запитання трохи катастрофою.
Калеб

@Caleb, Все зводиться до того, що питання є дуже незрозумілим, тому всі відповіді є різними тлумаченнями питання. На кожну відповідь я намагався сказати, на яке питання намагається відповісти.
Стефан Шазелас

Просто FYI: Спробував, awk '/./' file 1<>; fileякий спрацював. Для мене це навіть зрозуміліше, ніжsed '/./!d'
grebneke

5

Ось Perlодин вкладиш для цього:

perl -pi -e 's/^\s*\n//' yourfile

EDIT: Удосконалений код на основі коментарів ruakh нижче.


1
Абоperl -ni -e '/./ and print' yourfile
derobert

1
@peterph $є якорем (тобто нульовою шириною), тому він виключає новий рядок. Щодо зайвого простору - це причина, що я додав, що /xя не хотів Perlнамагатися інтерполювати `$ \` у регулярний вираз
Джозеф Р.

1
Вам не потрібно $, враховуючи, що у вас є \n. (Як варіант - вам не потрібно \n, враховуючи, що у вас є \s*та $; але, я думаю, s/^\s*\n//це стає зрозумілішим, що новий рядок видалено.) Вам також не потрібно /m; це не впливає на цю команду. І як тільки ви позбудетесь місця $та місця, вам це не знадобиться /x.
ruakh

1
@JosephR .: \nСам можна видалити; що ви не можете зробити, це видалити і те, $ і те \n. Так s/^\s*//би була проблема, яку ви описуєте, але s/^\s*$//було б добре, через \s*і $. (Ви бачите, що я маю на увазі?)
ruakh

1
@JosephR. Що трапляється, $ може збігатися перед новим рядком (за умови, що або /mпрапор увімкнено, або новий рядок є останнім символом рядка, або обом), але він також може відповідати кінці рядка. Наприклад, "abc" =~ m/^abc$/правда. У випадку з \s*$, цей \s*жадібний, щоб з'їсти новий рядок, а потім $відповідає кінці рядка. (Але я думаю s/^\s*\n//, все одно зрозуміліше, тому ваша відповідь настільки добре, як зараз.)
ruakh

5

Виходячи з уточнення в коментарях до вашого питання, щось на зразок:

awk -v RS= -v ORS= 1

може робити те, що ти хочеш.

Порожній роздільник записів - особливий випадок, який говорить про awkте, що записи мають бути абзацами (розділеними послідовностями порожніх рядків). Якщо встановити роздільник записів вихідних даних на порожній рядок, це означає, що вміст цих абзаців (без роздільників) повинен бути об'єднаним. 1- просто справжня умова друкувати кожен запис.

Це, однак, опустило б новий рядок, так що ви можете зробити:

awk -v RS= -v ORS= '1;END{if (NR) printf "\n"}'

3

Я знаю, що це було б простіше, якби я дав файл, але, на жаль, він містив конфіденційну інформацію, якою я не міг поділитися. Тим часом я написав мені рубіновий сценарій, який, здавалося, робив трюк:

require 'csv'
c = CSV.open("outfile1.csv", "w")
CSV.foreach("data.csv", :encoding => 'windows-1251:utf-8') do |row|
  row = row.map { |a| a.class == String ? a.gsub(/\r/, '') : a}
  c << row
end
c.close

Дякую всім за допомогу!


2
awk '
    length == 0 {next} 
    /^[^"]/ && /"$/ {print; next} 
    {printf("%s", $0)}
' filename

виробляє

"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"
"1", "2", "lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum ","2","3","4"

2

Я знайшов ідею для можливого рішення щодо stackoverflow .

sed -i ':a;N;$!ba;s/[^"]\n\s*\n/ /g' file.csv

Ви, ймовірно, повинні створити резервну копію вашого файлу csv, перш ніж тестувати його, але принаймні для прикладу, який ви забезпечили, що він працює бездоганно.

Хороше пояснення щодо внутрішнього опрацювання цього виразу пропонується у відповіді, я просто редагував його, щоб шукати рядки, які не закінчуються на "( [^"]\n).


1

Якщо з власної відповіді ви хочете видалити символи нового рядка, що містяться у цитованих рядках, ви можете зробити:

 perl -0777 -pe 's/".*?"/$_=$&;s:\n::g;$_/gse'

Ви також можете використовувати -iпрапор perl для редагування файлів на місці .

 perl -0777 -pe 's/".*?"/$_=$&;s:\n::g;$_/gse' file1 file2...

Або з GNU awk:

 awk -v RS=\" 'NR%2==0 {gsub("\n","")}; {printf "%s", $0 RT}'

або:

 awk -vRS=\" '1-NR%2{gsub("\n","")}{ORS=RT}1'

(якщо ви змагаєтесь за найкоротший)

Зауважте, що вони припускають, що у вхідних даних немає жодних уникнутих подвійних лапок.


0

Насправді схоже, що ви хочете більше, ніж видаляти порожні рядки, але видалити кожну послідовність з двох або більше символів нового рядка.

Що ви можете зробити з perl:

perl -0777 -pe 's/\n{2,}//gs' file

Ви також можете використовувати -iпрапор perl для редагування файлів на місці .

perl -0777 -pi -e 's/\n{2,}//gs' file1 file2...

0

Існує дедалі коротший спосіб видалення порожніх рядків у AWK:

awk 'NF' file

Але для отримання потрібного результату потрібен простий вкладиш:

awk 'NF {printf("%s ", $0); i++;} !(i % 2) {printf("\n");}' file

Пояснення

В AWK, порожній рядок означає, що рядок / запис не має полів, тобто NFзмінна (Кількість полів) дорівнює нулю. Один верхній вкладиш буде виконуватися лише при NF > 0друкуванні всіх рядків, але порожніх.

i++Є непорожній рядком лічильника.

!(i % 2)Використовується для того , щоб надрукувати два послідовних непусті рядки в шляху потрібного виходу, тобто, кожен раз , кратне 2 знайдено, moduloзаява !(i % 2)дає 1, то , що завершує конкатенацію двох непустих рядків.


Моє ліжко! Вибачте. Я не прочитав його цілого питання та потрібного результату. Відповідь виправлена ​​зараз. Спасибі. :-)
Марсело Аугусто

0

Ви можете використовувати Vim в режимі Ex:

ex -sc v/./d -cx b.csv
  1. v/./ знайти порожні рядки

  2. d видалити

  3. x зберегти і закрити

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.