質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

sed

sedとは、POSIX環境のために作られたコマンドラインエディタです。sedは編集スクリプトの指示のもとに複数のファイルを編集し、標準出力にその結果を出力します。

コマンドライン

コマンドライン(別名:Command Line Interface)は、ユーザに命令の入力を促す(プロンプト)文字列の表示を行い、すべての操作をキーボードを用いて文字列を打ち込む事でプログラムを走らせるユーザインターフェースです。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

AWK

AWKは、UNIX 上で開発されたプログラミング言語で、CSVファイルなどのテキストファイルの処理を目的にデザインされています。

Q&A

4回答

3533閲覧

不規則なデータファイルをコマンドラインでcsvファイルに整形したいです。どなたか教えていただけないでしょうか?

dlrowolleh

総合スコア120

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

sed

sedとは、POSIX環境のために作られたコマンドラインエディタです。sedは編集スクリプトの指示のもとに複数のファイルを編集し、標準出力にその結果を出力します。

コマンドライン

コマンドライン(別名:Command Line Interface)は、ユーザに命令の入力を促す(プロンプト)文字列の表示を行い、すべての操作をキーボードを用いて文字列を打ち込む事でプログラムを走らせるユーザインターフェースです。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

AWK

AWKは、UNIX 上で開発されたプログラミング言語で、CSVファイルなどのテキストファイルの処理を目的にデザインされています。

0グッド

0クリップ

投稿2017/08/31 05:24

編集2017/08/31 06:46

手作業で整形していたら時間がかかるようなファイルを
コマンド一行でcsvファイルに変換したいです。
どなたかエレガントなやり方を教えていただけないでしょうか?

sample1.txt

名前 あ 住所 住所あ 電話 電話あ 名前 い 電話 ※※ 年齢 年齢い 年齢 年齢う 名前 う 住所:住所う 電話 電話う 名前 ほげほげ 住所=住所え 電話 電話え 名前=名前お 電話 電話お 年齢 年齢お 住所 住所お

のような

・Tab、半角スペース、全角スペースで始まる

・ヘッダにしたい情報とデータとして扱いたい情報との間に
半角スペース、全角スペース、Tab,=,=,:がある
・不規則にデータが保存されている

ファイルsample1.txtがあります。
このファイルをresult.csvのようなcsv形式のファイルに整形したいのですが,
どなたかエレガントなやり方を教えていただけませんか?

result.csv

名前,年齢,住所,電話番号 あ,,住所あ,電話あ い,年齢い,,電話い う,年齢う,住所う,電話う 名前え,,住所え,電話え 名前お,年齢お,住所お,電話お

私は以下のようにコマンドを実行しました。

$ grep 名前 sample1.txt | cut -f2 -d ' ' | sed -e "s/$/,/" 2>&1 | tee 名前.txt
$ grep 年齢 sample1.txt | cut -f2 -d ' ' | sed -e "s/$/,/" 2>&1 | tee 年齢.txt
$ grep 住所 sample1.txt | cut -f2 -d ' ' | sed -e "s/$/,/" 2>&1 | tee 住所.txt
$ grep 電話 sample1.txt | cut -f2 -d ' ' | sed -e "s/$/,/" 2>&1 | tee 電話.txt
$ paste 名前.txt 年齢.txt 住所.txt 電話.txt >kekka.csv

その結果、以下のようなcsvファイルkekka.csvファイルができました。

名前, 年齢い, 住所あ, 電話あ,

い, 年齢 年齢う, 住所:住所う, ,
う, 年齢お, 住所=住所え, 電話う,
ほげほげ, 住所お, 電話え,
名前=名前お, 電話お,

問題点として

<sample1.txt・処理のやり方が原因?>

・Tab、半角スペース、全角スペースで始まる
・ヘッダにしたい情報とデータとして扱いたい情報との間に
半角スペース、全角スペース、Tab,=,=,:がある
<処理のやり方が原因?>
・csvファイルのヘッダ情報がない
・pasteした時にTabで勝手にインデントされている
・カンマ,の数が合っていない

が考えられます。

sample1.txtからresult.csvを作成するにはどのようにすればいいでしょうか?
こういったファイルをcsv形式にする機会が多く、最終的に手作業に頼ることになり、
時間がかかり毎回気が滅入っております。

アドバイスいただけましたら幸いです。
よろしくお願いします。

修正

result.csv

名前,年齢,住所,電話

あ,,住所あ,電話あ
い,年齢い,,※※
う,年齢う,住所う,電話う
ほげほげ,,住所え,電話え
名前お,年齢お,住所お,電話お

です
大変失礼いたしました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2017/08/31 05:52 編集

レコードの区切りはどういう基準で行いますか?
dlrowolleh

2017/08/31 06:35 編集

大変失礼いたしました。result.csvを書き間違えていました。 「ほげほげ」「電話い」はsample1.txtにありませんでした。 質問を修正します。
guest

回答4

0

こういうのはどうでしょう。今、ちょっと忙しいので、書きっぱなしでろくに検証していませんが。

awk

1BEGIN { 2 no_of_fields = split("名前,年齢,住所,電話", keys, ","); 3 for (i = 1; i <= no_of_fields; i++) { 4 key2index[keys[i]] = i; 5 } 6 line = ""; 7 for (i = 1; i <= no_of_fields; i++) { 8 line = line "," keys[i]; 9 } 10 print substr(line, 2); 11} 12 13{ 14 key = $1; 15 idx = key2index[key]; 16 val = $2; 17} 18 19{ 20 if (fields[idx] != "") { 21 flush(); 22 } 23 fields[idx] = val; 24 vals++; 25} 26 27END { 28 if (vals > 0) { 29 flush(); 30 } 31} 32 33function flush() { 34 line = ""; 35 for (i = 1; i <= no_of_fields; i++) { 36 line = line "," fields[i]; 37 fields[i] = ""; 38 } 39 print substr(line, 2); 40 vals = 0; 41}

実行は次のように。

bash

1$ sed -e 's/^\s\+//g' -e 's/[\s:==]/ /g' sample1.txt | awk -f pretify.awk 2名前,年齢,住所,電話 3あ,,住所あ,電話あ 4い,年齢い,,※※ 5う,年齢う,住所う,電話う 6ほげほげ,,住所え,電話え 7名前お,年齢お,住所お,電話お

投稿2017/08/31 06:16

unau

総合スコア2468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

根本的な問題として、レコードを区別する基準が不明瞭のようです。

名前 ほげほげ 住所=住所え 電話 電話え 名前=名前お 電話 電話お

これは1件としますか?2件としますか?
上記のgrepの処理では、名前ー住所その他の対応関係をつけることが全くできないように思います。

投稿2017/08/31 05:38

KojiDoi

総合スコア13671

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dlrowolleh

2017/08/31 06:37

ご回答いただきありがとうございます。 >根本的な問題として、レコードを区別する基準が不明瞭のようです。 おっしゃるとおりでした。 大変失礼いたしました。result.csvを書き間違えていました。 「ほげほげ」「電話い」はsample1.txtにありませんでした。 質問を修正します。
KojiDoi

2017/08/31 06:56

依然として疑問を解消できません。 あと、「年齢 年齢う」はどのレコードに入るのですか? 言い換えると、「空行をレコードの区切りとする」・「名前が出てきたら新しいレコードの始まりとする」の2つの可能性が考えられるように思いますが、どちらですか? それともそれ以外の基準がありますか?
guest

0

フローとしては以下のような感じでしょうか。

(1)先頭の不要スペースを削除する
(2)区切り文字を統一する
(3)空行から空行までを一まとまりとして処理
ハッシュを利用し、ヘッダ名称と値をそれぞれ格納
一まとまりが終わるまで上記を実施

ヘッダ順にカンマを挿入しながら出力

text

1年齢 年齢う 2 3名前 う 4住所:住所う 5電話 電話う

残念ながら上記がうまく処理できません。
これはヘッダが全て存在しているという過程が成り立てば処理ができますが、そうでない場合、どのように一まとまりと判断するか適切に判断できないためです。

text

1名前 ほげほげ 2住所=住所え 3 電話 電話え 4名前=名前お 5電話 電話お 6 7年齢 年齢お 8住所 住所お

こちらもKojiDoiさんが指摘の通りです。
先の問題は、名前がない場合は次の塊も同じブロックと判断するとか、あるかもしれませんが、次の問題は次のブロックと判断したいところですが、先の問題との兼ね合いでうまく両者を同じように処理できないと思われます。

まとめると、ヘッダが全てあるわけではなく、その順番も決まっていないため、一つのブロックとして扱うべきデータの分離ができないことです。
何かしらそれができる方法、ルールが必要で、それを上記のステップのいずれかに入れる必要があります。基本的には(3)になります。

投稿2017/09/01 05:05

t_obara

総合スコア5488

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ご提示の例とお答えと同じになる様にはしてみました。

bash

1echo 名前,年齢,住所,電話 2sed -e"s/^[ \t]*//" -e"s/\t/ /g" -e"s/[==:]/ /" sample1.txt | 3awk '{if($1 in v){printf"%s,%s,%s,%s\n",v["名前"],v["年齢"],v["住所"],v["電話"];delete v;}v[$1]=$2}END{printf"%s,%s,%s,%s\n",v["名前"],v["年齢"],v["住所"],v["電話"]}'

投稿2017/08/31 07:34

編集2017/09/01 05:09
A.Ichi

総合スコア4070

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問