Cách tốt để lọc một tệp văn bản để loại bỏ các dòng trống?


11

Tôi có tệp .csv (trên máy mac) có một loạt các dòng trống, ví dụ:

"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"

Mà tôi muốn chuyển đổi thành:

"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"

Tôi biết phải có một lớp lót nhưng tôi không biết awk hay sed. Bất kỳ lời khuyên đánh giá rất cao!


1
Theo mẫu đó, bạn thực sự muốn loại bỏ các ngắt dòng nhúng khỏi các trường. Đúng không? Nói cách khác, có 6 dòng đầu vào và nên là 2 dòng đầu ra?
manatwork

Vâng, đó chính xác là những gì tôi đang cố gắng loại bỏ: các dòng mới được nhúng bên trong một chuỗi trích dẫn.
pitosalas

Vì vậy, những gì bạn cần là một cái gì đó loại bỏ dòng mới trong dấu ngoặc kép. Điều đó sẽ phức tạp hơn một chút, bởi vì bạn cần regex multiline.
tongpu

Câu trả lời:


11

Bạn có thể sử dụng -vchế độ (đảo ngược) của grep để làm điều này:

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

Lưu ý rằng những tệp đó cần phải là các tệp khác nhau, do cách chuyển hướng shell hoạt động. Tệp đầu ra được mở (và làm trống) trước khi tệp đầu vào được đọc. Nếu bạn có nhiều hơn (không phải mặc định trên Mac OS X), bạn có thể sử dụng spongeđể khắc phục điều này:

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

Nhưng tất nhiên, sau đó bạn có một thời gian khó khăn hơn để trở lại nếu có sự cố.

Nếu bạn "dòng trống" thực sự có thể chứa khoảng trắng (có vẻ như chúng làm), thì bạn có thể sử dụng thay thế này:

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

Điều đó sẽ bỏ qua các dòng trống cũng như các dòng chỉ chứa khoảng trắng. Tất nhiên bạn có thể thực hiện spongechuyển đổi tương tự trên nó.


Cảm ơn .... Không xóa bất kỳ dòng trống nào ... Có lẽ ^ $ không khớp? Nhưng các dòng trống rỗng theo sự hiểu biết tốt nhất của tôi. Hãy nhớ rằng đây là một cv được tạo bởi excel trên máy mac ... Điều đó có nói gì không? (Đừng chạy đi la hét vì tôi đã nói Excel :)
pitosalas

@pitosalas Có lẽ chúng không phải là dòng trống. Hãy thử thay đổi nó thành egrep -v '^[[:space:]]*$'... lưu ý grep -> egrep và mẫu mới lạ
derobert

Không làm việc. Đã xóa một loạt các trích dẫn kép và làm cho một mớ hỗn độn ...
pitosalas

@pitosalas Tôi không chắc nó sẽ xóa dấu ngoặc kép như thế nào. Nó chỉ có thể xóa khoảng trắng. Và thực sự, đó là những gì nó làm khi tôi kiểm tra nó trên dữ liệu mẫu mà bạn đã đăng ...
derobert

@pitosalas bạn có thể kiểm tra xem một trong hai lệnh này có phát ra thứ gì đó có vẻ hợp lý không (trái ngược với tiếng vô nghĩa): iconv -f utf16le file.csv | headhoặciconv -f utf16be file.csv | head
derobert

8

Tùy chọn dễ nhất là chỉ grep .. Ở đây, dấu chấm có nghĩa là "khớp bất cứ thứ gì", vì vậy nếu dòng trống, nó không khớp. Mặt khác, nó in toàn bộ dòng.


6

Để xóa các dòng trống, tại chỗ , với ksh93:

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

Các <>;nhà điều hành chuyển hướng là cụ thể cho ksh93 và cũng giống như các tiêu chuẩn <>điều hành ngoại trừ việc sẽ cắt cụt ksh tập tin sau khi lệnh đã chấm dứt.

sed '/./!d'là một cách viết phức tạp grep ., nhưng không may là GNU grep ít nhất phàn nàn nếu thiết bị xuất chuẩn của nó trỏ đến cùng một tệp với stdin của nó. Bạn muốn nói người ta có thể viết:

grep . file | cat 1<>; file

Nhưng thật không may, có một lỗi trong ksh93 (ít nhất là phiên bản của tôi (93u +)), trong đó tệp dường như bị cắt ngắn đến độ dài bằng không trong trường hợp đó.

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

Có vẻ như đã khắc phục được lỗi đó, nhưng bây giờ, nó phức tạp hơn nhiều so với lệnh sed.


Vui lòng kết hợp câu trả lời của bạn thành một mục được định dạng tốt với hướng dẫn nhanh khi nào nên sử dụng từng giải pháp. Các cách tiếp cận khác nhau cho các vấn đề khác nhau tất cả cùng lộn xộn trong các câu trả lời nổi đã khiến câu hỏi này trở thành một thảm họa để đọc.
Caleb

@Caleb, Tất cả sôi sục cho câu hỏi rất không rõ ràng, vì vậy tất cả các câu trả lời của mọi người là dành cho các cách hiểu khác nhau của câu hỏi. Đối với mỗi câu trả lời, tôi cố gắng nói câu hỏi nào nó cố gắng trả lời.
Stéphane Chazelas

Chỉ cần FYI: Đã thử awk '/./' file 1<>; filemà làm việc. Đối với tôi, điều đó thậm chí còn rõ ràng hơnsed '/./!d'
grebneke

5

Đây là một Perllớp lót cho nó:

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

EDIT: Cải thiện mã dựa trên ý kiến ​​của ruakh bên dưới.


1
Hoặcperl -ni -e '/./ and print' yourfile
derobert

1
@peterph $là một neo (tức là độ rộng bằng không) vì vậy nó loại trừ dòng mới. Đối với không gian thừa, đó là lý do tôi thêm vào /xTôi không muốn Perlthử nội suy `$ \` vào regex
Joseph R.

1
Bạn không cần $, cho rằng bạn có \n. (Ngoài ra - bạn không cần \n, cho rằng bạn có \s*$; nhưng tôi nghĩ s/^\s*\n//làm rõ hơn rằng dòng mới bị xóa.) Bạn cũng không cần /m; nó không có tác dụng với lệnh này Và một khi bạn thoát khỏi $và không gian, bạn sẽ không cần /x.
ruakh

1
@JosephR.: Bản \nthân nó có thể được gỡ bỏ; những gì bạn không thể làm là loại bỏ tất cả các $ các \n. Vì vậy, s/^\s*//sẽ có vấn đề bạn mô tả, nhưng s/^\s*$//sẽ ổn thôi, bởi vì \s*$. (Bạn có hiểu ý tôi không?)
ruakh

1
@JosephR.: Điều gì xảy ra là, $ có thể khớp trước một dòng mới (với điều kiện là /mcờ được bật hoặc dòng mới là ký tự cuối cùng của chuỗi hoặc cả hai), nhưng nó cũng có thể khớp với cuối chuỗi. Ví dụ, "abc" =~ m/^abc$/là đúng. Trong trường hợp \s*$, cái đó \s*đủ tham lam để ăn hết dòng mới, và sau đó $khớp với chuỗi cuối. (Nhưng tôi nghĩ s/^\s*\n//là rõ ràng hơn, dù sao đi nữa, vì vậy câu trả lời của bạn vẫn tốt như bây giờ.)
ruakh

5

Dựa trên sự làm rõ trong các bình luận cho câu hỏi của bạn, đại loại như:

awk -v RS= -v ORS= 1

có thể làm những gì bạn muốn

Dấu tách bản ghi trống là trường hợp đặc biệt cho biết awkcác bản ghi phải là các đoạn văn (được phân tách bằng các chuỗi dòng trống). Đặt dấu tách bản ghi đầu ra thành chuỗi trống cũng có nghĩa là nội dung của các đoạn đó (không có dấu phân cách) sẽ được nối. 1chỉ là một điều kiện thực sự để in mọi hồ sơ.

Tuy nhiên, điều đó sẽ bỏ qua dòng mới, vì vậy bạn có thể làm:

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

3

Tôi biết việc này sẽ dễ dàng hơn nếu tôi đưa tập tin, nhưng thật không may, nó chứa thông tin bí mật mà tôi không thể chia sẻ. Trong khi đó, tôi đã viết cho tôi một kịch bản ruby ​​dường như thực hiện mánh khóe:

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

Cảm ơn mọi người đã giúp đỡ!


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

sản xuất

"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

Tôi tìm thấy một ý tưởng cho một giải pháp khả thi trên stackoverflow .

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

Bạn có thể nên sao lưu tệp csv của mình trước khi kiểm tra nó, nhưng ít nhất là ví dụ bạn cung cấp nó hoạt động hoàn hảo.

Một lời giải thích tốt về hoạt động bên trong của biểu thức này được đưa ra ở câu trả lời, tôi chỉ chỉnh sửa nó để tìm các dòng không kết thúc bằng dấu "( [^"]\n).


1

Nếu, từ phản hồi của riêng bạn, bạn muốn xóa các ký tự dòng mới có trong các chuỗi được trích dẫn, bạn có thể làm:

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

Bạn cũng có thể sử dụng -icờ của perl để chỉnh sửa các tệp tại chỗ .

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

Hoặc với GNU awk:

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

hoặc là:

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

(nếu bạn đang thi đấu ngắn nhất)

Lưu ý rằng những người cho rằng không có ký tự trích dẫn kép thoát trong đầu vào.


0

Có vẻ như bạn muốn nhiều hơn là xóa các dòng trống, nhưng loại bỏ mọi chuỗi gồm 2 hoặc nhiều ký tự dòng mới.

Những gì bạn có thể làm với perl:

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

Bạn cũng có thể sử dụng -icờ của perl để chỉnh sửa các tệp tại chỗ .

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

0

Có một cách ngắn hơn để loại bỏ các dòng trống trong AWK:

awk 'NF' file

Nhưng để có được đầu ra mà bạn muốn, tất cả những gì cần thiết là một lớp lót đơn giản:

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

Giải trình

Trong AWK, một dòng trống có nghĩa là hàng / bản ghi không có trường, nghĩa là biến NF(Số lượng trường) bằng không. Một lớp lót ở trên sẽ chỉ thực hiện khi NF > 0, in tất cả các dòng, nhưng các dòng trống.

Đây i++là bộ đếm dòng không trống.

Chúng !(i % 2)được sử dụng để in hai dòng không trống liên tiếp theo cách đầu ra mong muốn của bạn, nghĩa là, mỗi khi tìm thấy bội số của 2, modulocâu lệnh !(i % 2)sẽ tạo ra 1, kết thúc nối hai dòng không trống.


Lỗi của tôi! Lấy làm tiếc. Tôi đã không đọc toàn bộ câu hỏi của anh ấy và đầu ra mong muốn. Câu trả lời đã được sửa. Cảm ơn. :-)
Marcelo Augusto

0

Bạn có thể sử dụng Vim trong chế độ Ex:

ex -sc v/./d -cx b.csv
  1. v/./ tìm dòng trống

  2. d xóa bỏ

  3. x lưu và đóng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.