Какой хороший способ фильтрации текстового файла для удаления пустых строк?

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. Любые советы с благодарностью!

pitosalas
источник
1
В соответствии с этим примером вы действительно хотите удалить встроенные разрывы строк из полей. Это правильно? Другими словами, есть 6 входных строк и должно быть 2 выходные строки?
manatwork
Да, это именно то, от чего я пытаюсь избавиться: встроенные символы новой строки внутри строки в кавычках.
питосалась
Так что вам нужно что-то, что удаляет символы новой строки внутри кавычек. Это будет немного сложнее, потому что вам нужно многострочное регулярное выражение.
Тонгпу

Ответы:

11

Вы можете использовать -vрежим grep (invert match), чтобы сделать это:

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

Обратите внимание, что это должны быть разные файлы из-за того, как работают перенаправления оболочки. Выходной файл открывается (и очищается) до его чтения. Если у вас есть moreutils (не по умолчанию в Mac OS X), вы можете использовать, spongeчтобы обойти это:

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

Но, конечно, тогда вам будет труднее вернуться назад, если что-то пойдет не так.

Если вы «пустые строки» на самом деле можете содержать пробелы (звучит так, как они), то вы можете использовать это вместо:

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

Это будет игнорировать пустые строки, а также строки, содержащие только пробелы. Конечно, вы можете сделать то же самое spongeпреобразование на нем.

derobert
источник
Спасибо .... Не удалил ни одной пустой строки ... Может быть, ^ $ не соответствует? Но строки пустые, насколько мне известно. Помните, что это CDV, созданный Excel на Mac ... Это что-нибудь говорит? (Не убегайте, крича, потому что я сказал Excel :)
pitosalas
@pitosalas Они, вероятно, не пустые строки. Попробуйте изменить его на egrep -v '^[[:space:]]*$'... note grep -> egrep и странный новый шаблон
derobert
Не работал. Удалил кучу двойных кавычек и сделал беспорядок ...
pitosalas
@pitosalas Я не уверен, как он удалит двойные кавычки. Он должен иметь возможность только удалить пробелы. И действительно, это то, что он делает, когда я тестирую его на примере данных, которые вы опубликовали ...
Дероберт
@pitosalas, не могли бы вы проверить, выплевывает ли какая-либо из этих команд что-то, что выглядит разумно (в отличие от тарабарщины): iconv -f utf16le file.csv | headилиiconv -f utf16be file.csv | head
derobert
8

Самый простой вариант просто grep .. Здесь точка означает «соответствовать чему угодно», поэтому, если строка пуста, она не соответствует. В противном случае он печатает всю строку как есть.

Onturenio
источник
6

Для того, чтобы удалить пустые строки, в месте , с ksh93:

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

Оператор <>;перенаправления специфичен для ksh93 и аналогичен стандартному <>оператору, за исключением того, что ksh усекает файл после завершения команды.

sed '/./!d'это запутанный способ написания grep ., но, к сожалению, GNU grep по крайней мере жалуется, если его стандартный вывод указывает на тот же файл, что и его стандартный ввод. Вы бы сказали, что можно написать:

grep . file | cat 1<>; file

Но, к сожалению, в ksh93 есть ошибка (по крайней мере, в моей версии (93u +)), в которой файл кажется обрезанным до нулевой длины в этом случае.

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

Кажется, обойти эту ошибку, но теперь, это гораздо более запутанный, чем команда sed.

Стефан Шазелас
источник
Пожалуйста, объедините ваши ответы в одну хорошо отформатированную запись с кратким руководством, когда следует использовать каждое решение. Разные подходы к различным проблемам, смешанные в плавающих ответах, сделали этот вопрос бедствием для чтения.
Калеб
@Caleb, все сводится к тому, что вопрос очень неясен, так что все ответы каждого на разные интерпретации вопроса. Для каждого ответа я пытался сказать, на какой вопрос он пытается ответить.
Стефан Шазелас
Просто к вашему сведению: попробовал, awk '/./' file 1<>; fileкоторый работал. Для меня это даже яснее, чемsed '/./!d'
Гребнеке
5

Вот Perlодна строка для этого:

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

РЕДАКТИРОВАТЬ: Улучшен код, основанный на комментариях ruakh ниже.

Джозеф Р.
источник
1
Илиperl -ni -e '/./ and print' yourfile
Дероберт
1
@peterph $- это якорь (то есть с нулевой шириной), поэтому он исключает символ новой строки. Что касается лишнего пространства, это причина, по которой я добавил, что /xя не хотел Perlпытаться интерполировать `$ \` в регулярное выражение
Джозеф Р.
1
Вам не нужно $, учитывая, что у вас есть \n. (В качестве альтернативы - вам не нужно \n, учитывая, что у вас есть \s*и $;; но я думаю, что s/^\s*\n//становится понятнее, что новая строка удаляется.) Вам также не нужно /m; это не влияет на эту команду. И как только вы избавитесь от $и пространства, вам не понадобится /x.
Руах
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

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

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

Спасибо всем за помощь!

pitosalas
источник
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

Я нашел идею для возможного решения на стеке потока .

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

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

Хорошее объяснение внутренней работы этого выражения предлагается в ответе, я просто отредактировал его, чтобы искать строки, которые не заканчиваются на "( [^"]\n).

Tongpu
источник
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

По сути, похоже, что вы хотите больше, чем удаление пустых строк, но удалить каждую последовательность из 2 или более символов новой строки.

Что вы могли бы сделать с 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 сохрани и закрой

Стивен Пенни
источник