Xử lý văn bản - nối mỗi hai dòng bằng dấu phẩy


35

Tôi có hơn 1000 dòng trong một tập tin. Tệp bắt đầu như sau (số dòng được thêm):

Station Name
Station Code
A N DEV NAGAR
ACND
ABHAIPUR
AHA
ABOHAR
ABS
ABU ROAD
ABR

Tôi cần chuyển đổi tệp này thành một tệp, với các mục được phân tách bằng dấu phẩy bằng cách nối mỗi hai dòng. Dữ liệu cuối cùng sẽ giống như

Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
...

Những gì tôi đã cố gắng là - cố gắng viết một kịch bản shell và sau đó echochúng với dấu phẩy ở giữa. Nhưng tôi đoán một lớp lót hiệu quả đơn giản hơn sẽ thực hiện công việc ở đây có thể là trong sed/ awk.

Có ý kiến ​​gì không?


@ l0b0 Bạn đã chỉnh sửa nhận xét của OP rằng các số dòng "chỉ ở đó để giải thích" ...
jasonwryan

@jasonwryan Xin lỗi, tôi nghĩ rằng các dòng ở đó để giải thích. Lỗi phân tích cú pháp tại dòng 0.
l0b0

Câu trả lời:


39

Chỉ cần sử dụng cat(nếu bạn thích mèo ;-)) và paste:

cat file.in | paste -d, - - > file.out

Giải thích: pasteđọc từ một số tệp và dán cùng các dòng tương ứng (dòng 1 từ tệp đầu tiên với dòng 1 từ tệp thứ hai, v.v.):

paste file1 file2 ...

Thay vì tên tệp, chúng ta có thể sử dụng -(dấu gạch ngang). pastelấy dòng đầu tiên từ file1 (đó là stdin). Sau đó, nó muốn đọc dòng đầu tiên từ file2 (cũng là stdin). Tuy nhiên, vì dòng stdin đầu tiên đã được đọc và xử lý, nên cái đang chờ trên luồng đầu vào là dòng thứ hai của stdin, pastevui vẻ dán vào dòng đầu tiên. Các -dtùy chọn đặt dấu phân cách để trở thành một dấu phẩy chứ không phải là một tab.

Ngoài ra, làm

cat file.in | sed "N;s/\n/,/" > file.out

PS Có, người ta có thể đơn giản hóa những điều trên để

< file.in sed "N;s/\n/,/" > file.out

hoặc là

< file.in paste -d, - - > file.out

trong đó có lợi thế của việc không sử dụng cat.

Tuy nhiên, tôi đã không sử dụng thành ngữ này cho mục đích rõ ràng, vì lý do rõ ràng - nó ít dài dòng hơn và tôi thích cat(CATS LÀ NICE). Vì vậy xin vui lòng không chỉnh sửa.

Ngoài ra, nếu bạn thích dán vào mèo (dán là lệnh ghép các tệp theo chiều ngang, trong khi mèo nối chúng theo chiều dọc), bạn có thể sử dụng:

paste file.in | paste -d, - -

Chỉ cần đề cập đến nó một lần nữa. Số dòng không phải là một phần của tệp :)
mtk

Các paste lệnh một cách hoàn hảo làm việc, bạn có thể xin vui lòng cho một lời giải thích ít nhiều về nó. Các dấu gạch ngang ???
mtk

2
Các dấu gạch ngang có nghĩa là "đọc từ stdin". Nếu cùng một nguồn đầu vào được lặp lại, dán sẽ biết đọc từ nó nhiều lần trên mỗi hàng đầu ra.
dubiousjim

@sch: chỉnh sửa tuyệt vời, tôi sẽ không chạm vào nó :-)
tháng 1 năm

1
Đối với catlập luận của bạn . Không sed "N;s/\n/,/" file.in > file.outhoạt động?
Bernhard

8

Trong trường hợp bất kỳ ai hạ cánh ở đây đang tìm cách kết hợp tất cả các dòng vào một lớp lót CSV, hãy thử

cat file | tr '\n' ','

3
sed 'N;s/\n/,/' file

Sử dụng sed, nối (N) cứ sau 2 dòng và thay thế dòng mới (\ n) bằng ",".


3
paste -sd ',\n' file.in > file.out

Cũng lưu ý rằng vì chúng tôi chỉ thay thế một ký tự bằng một ký tự khác (mọi dòng mới khác bằng dấu phẩy), chúng tôi có thể làm việc trên tệp đầu vào tại chỗ:

paste -sd ',\n' file.in 1<> file.in

(nhưng hãy cẩn thận, nó có thể không hoạt động trên các hệ thống không phải Unix có bộ kết thúc CRLF (như của Microsoft) mà một số POSIX được mô phỏng pastecó thể xử lý theo cách không phải Unix)


Điều đó 1đang làm gì ở đây 1<>? đó có phải là một lỗi đánh máy không?
αғsнιη

@ αsнι, xem cái này
iruvar

@iruvar cảm ơn bạn
αғsнι

2

Đây là một lớp lót (mặc dù có khả năng hàng triệu lệnh-run-er) sử dụng Bash thuần túy:

(IFS=; while read -r name; do read -r code; printf '%s\n" "$name,$code"; done < file.in) > file.out

Tôi sử dụng một lớp con (phần phụ) để tôi sẽ không phải lưu trữ và khôi phục IFS. Cái nào khác nên làm để không làm xáo trộn môi trường người dùng trong trường hợp nguồn có nguồn gốc. Thay thế sẽ là để vượt qua IFS mới chỉ readnhư trong IFS= read -r name, IFS= read -r code.

Thực tế là tất cả các lệnh trong vòng lặp được xây dựng trong trình bao làm cho hiệu năng của nó có thể chấp nhận được và thậm chí còn nhanh hơn các giải pháp khác cho các tệp nhỏ. Nhưng nhiều người sẽ coi đó là một thực hành tồi và người ta nên cẩn thận khi khái quát nó với bất cứ điều gì khác.


nói chung yay cho việc sử dụng các subshells để bản địa hóa các thay đổi môi trường. Nhưng trong trường hợp này thì không cần thiết: thay vào đó bạn có thể làm while IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.in, đó là một thành ngữ tôi thường thấy trong các kịch bản shell. Các -rlá cờ để readcó nghĩa là "giải thích các nhân vật '\' tiếp theo là nhân vật 'n' trong dòng stdin như hai nhân vật, chứ không phải là một dòng mới." Có thể cho rằng, có thể thẩm mỹ hơn để tạo ra lớp con như bạn làm hơn là lặp lại IFS='\n'.
dubiousjim

@dubiousjim: -rGiải pháp cải tiến về mặt kỹ thuật. Tuyệt quá! Tôi không phải là người hâm mộ ý tưởng vượt qua IFShai lần thay đổi . Nếu tôi đã sử dụng một lần đọc, siêu đẹp, nhưng không hai lần. Tất nhiên đó là vấn đề quan điểm . Sử dụng một lớp con là một chút so với kiến ​​thức chung của Bash mà tôi muốn nói, vì vậy rất nhiều người sẽ gặp khó khăn trong việc hiểu mục đích của nó. Đó là một điều xấu.
Đã xóa

2

Đối với bộ câu trả lời hoàn chỉnh, một awkgiải pháp khả thi có thể là:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*

@downvoter: Điều gì sai với câu trả lời của tôi để xứng đáng với một downvote? Làm thế nào nó có thể được cải thiện?
Bernhard

Có lẽ vì lười printf? Sẽ thất bại trong trường hợp hiếm khi tên trạm chứa định danh định dạng. (Xem pastebin.com/wgxFttrJ để biết ví dụ.) Nhưng đây chỉ là dự đoán, downvote không phải từ tôi.
manatwork

1

Hạt dẻ già của một awkthành ngữ

awk '{ORS=NR%2?",":"\n";print}' file
Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR

awk '{ORS=NR%2?",":"\n"};1'là thành ngữ ngắn hơn và nhiều hơn
cuonglm

@cuonglm, tôi nghi ngờ điều đó. Trong trường hợp này, nó vẫn là một lớp lót mặc dù printvà ý định rõ ràng. 1rõ ràng với những awkbàn tay cũ như tôi nhưng tôi thíchprint
iruvar

Đây là giải pháp đơn giản đầu tiên mà tôi thấy có thể dễ dàng cấu hình đến hơn 2 dòng. Tôi đã chiến đấu sedmột lúc trước khi tìm kiếm, nhưng awklàm cho việc kết hợp cứ sau 4 dòng dễ dàng hơn. Tiết kiệm cho tôi một chuyến đi đến $EDITOR!
opello

0

Có thể với perl quá,

perl -pe 's/^\d+\.\s+//;$.&1?chomp:print","' file


0

Ví dụ:

seq 0 70 | xargs -L 2 | sed 's/ /,/g'

Đầu ra: (lưu ý: xargs -L number_of_columnshoạt động độc đáo với hầu hết số lượng cột không chỉ mỗi hai dòng)

0,1
2,3
4,5
6,7
8,9
10,11
12,13
14,15
16,17
18,19
20,21
22,23
24,25
26,27
28,29
30,31
32,33
34,35
36,37
38,39
40,41
42,43
44,45
46,47
48,49
50,51
52,53
54,55
56,57
58,59
60,61
62,63
64,65
66,67
68,69
70

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.