こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

awk or perlが詳しい方ご教授下さい。

お世話になります。
下記のようなファイル内容があるとします。
(あくまでも例です。実際はもっと複雑です。)
この内容の大阪府に関する部分だけを抽出したいと思います。
抽出方法はawkで抽出行数を指定する方法を考えていますが、
得られている情報は「大阪府 end」の行数だけです。
「大阪府 start」の行数を取得出来れば目的は達成出来るのですが、
その方法が分かりません。
awkで指定行数から上方向に検索をかけるということは可能でしょうか?
(下記例でいくと「大阪府 end」の行数から「大阪府 start」を検索し、
その行数を取得するということをやりたいです。)
可能であればその方法をご教授下さい。

awkで無理ならperlでの方法でも構いません。

<例>
●ファイルの中身
東京都 start
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
1000円
東京都 end
大阪府 start
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
10000円
大阪府 end
神奈川県 start
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
20000円
神奈川県 end
北海道 start
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
3000円
北海道 end




以上、宜しくお願いします。

投稿日時 - 2006-09-13 21:06:52

QNo.2404498

すぐに回答ほしいです

質問者が選んだベストアンサー

↓な感じの事をしたいという事?
今、手元にSolarisの環境がないからFreeBSDでの実行です。

%cat data
大阪府 start
淀川支店
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
10000円
大阪府 end
大阪府 start
天王寺支店
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
1000円
大阪府 end
大阪府 start
住吉支店
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
DDDDDDDDDDDDDDDDDDD
0円
大阪府 end

%cat sample.sh
#!/bin/sh

branch="$1"

datafile=data

sedopt=`awk -v Flag=0 '
/start$/{Start=NR}
/'${branch}'/{Flag=1}
/end$/{if(Flag){printf("%d,%dp",Start,NR);exit}}' ${datafile}`

sed -n ${sedopt} ${datafile}

%./sample.sh 天王寺支店
大阪府 start
天王寺支店
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
1000円
大阪府 end

投稿日時 - 2006-09-14 19:05:04

お礼

ご回答ありがとうございます。
頂いたソースを実行するとエラーとなってしまいます。
現在エラー個所特定中です。
また質問させて下さい。

投稿日時 - 2006-09-15 20:04:44

ANo.4

このQ&Aは役に立ちましたか?

10人が「このQ&Aが役に立った」と投票しています

回答(12)

ANo.12

>以下の「;Flag=0」はどういう意味なのでしょうか?

>{if(Flag){printf("%d,%d ",Start,NR);Flag=0}}

下記、実行例のように「天王寺支店」を含む「~start」「~end」の組が複数存在する場合に対応するためです。
引数で指定した支店名を見つけるとFlagに1を設定しますので、「~end」を見つけた後、次の一致するものを探すために初期化しています。
複数存在する事はないという事なら、このままだと見つけた以降も余計な読み込みが発生しますので exit で終了にしていいと思います。

>% ./sample.sh 天王寺支店
1つ目の「大阪府 start」「大阪府 end」組
>大阪府 start
>天王寺支店
>CCCCCCCCCCCCCCCCCCC
>DDDDDDDDDDDDDDDDDDD
>1000円
>大阪府 end

2つ目の「大阪府 start」「大阪府 end」組
>大阪府 start
>天王寺支店
>EEEEEEEEEEEEEEEEEEE
>FFFFFFFFFFFFFFFFFFF
>1500円
>大阪府 end

投稿日時 - 2006-09-21 14:50:36

お礼

初期化ですか。理解しました。
ありがとうございます。

投稿日時 - 2006-09-28 02:03:59

ANo.11

#6です。

> awk: 構文エラー (3 行目の周辺)
> awk: 不正な文を検出しました。 (3 行目の周辺)
> awk: 構文エラー (8 行目の周辺)
> awk: 不正な文を検出しました。 (8 行目の周辺)
> awk: 構文エラー (10 行目の周辺)
> awk: 不正な文を検出しました。 (10 行目の周辺)

ふうむif文全部引っかかってますね。一応gawkの awk 互換モードで
チェックしたので構文的な問題はないはずなんですが…

とりあえず、nawk(Solarisにはこれもあるはず)ではどうでしょうか?

あと、if文を

if (/^大阪府 start$/) { cnt=0 }

if (/^大阪府 end$/ && exit_flag==1) { exit(0) }
if (/^天王寺支店$/) { exit_flag = 1 }

のようにブレースで本体を囲むと状況は変わりますか?

投稿日時 - 2006-09-20 23:53:05

ANo.10

#6,8 です。

> awk: 構文エラー (1 行目の周辺)
> awk: 処理を中止します。 (1 行目の周辺)

というエラーとのことですが

#!/usr/bin/awk
/^大阪府 start$/, /^大阪府 end$/ {

多分、#の行を

#!/usr/bin/awk -f

のように -f を末尾につければ解決するのではないでしょうか。
すいません前回見落としてました。

投稿日時 - 2006-09-19 23:59:51

お礼

ご回答ありがとうございます。
-fを付けたところ以前出力したエラーが表示されました。
if文のところがエラーになります。何故でしょうか?

<エラー>
awk: 構文エラー (3 行目の周辺)
awk: 不正な文を検出しました。 (3 行目の周辺)
awk: 構文エラー (8 行目の周辺)
awk: 不正な文を検出しました。 (8 行目の周辺)
awk: 構文エラー (10 行目の周辺)
awk: 不正な文を検出しました。 (10 行目の周辺)

投稿日時 - 2006-09-20 21:36:09

ANo.9

xjd

こんばんは。

回答のほとんどが、awkなので、ためしに、Perl で作成してみました。
よろしかったら、参考にしてください。


# cat data.pl

#!/usr/bin/perl

@line = ();

while (<>) { # 標準入力より1レコード読む
  if ( /^大阪府 start$/ .. /^大阪府 end$/ ) { # 大阪府ブロックの中の時

    push(@line, $_); # 大阪府ブロックの1レコードを配列変数に追加

    if ( /^大阪府 end$/ ) { # 大阪府ブロックが終了の時
      if ($line[1] =~ /天王寺支店/) { # 大阪府ブロックの2レコード目が天王寺支店なら
        print @line;
        print "***** 行数 = ", $#line+1, "\n";
      }
      @line = (); # 配列変数の初期化
    }
  }
}


# ./data.pl < data.txt

大阪府 start
天王寺支店
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
1000円
大阪府 end
***** 行数 = 6


配列変数(@line) には、ブロックの各レコードが1行ずつ代入されているので、
必要なレコードを取り出して処理できます。


配列変数(@line)の中身

$line[0] = 大阪府 start
$line[1] = 天王寺支店
$line[2] = CCCCCCCCCCCCCCCCCCC
......
$line[$#line-1] = 1000円
$line[$#line] = 大阪府 end

($#line は、配列変数@line の添え字の最大値を返します。)

投稿日時 - 2006-09-18 00:00:59

ANo.8

#6 です。
if文でエラーになるとのことですが、どんなエラーでしょう?
構文エラーですか?

Solrarisを使える環境にないのでそのawkでは試していませんが、
一応動作も確認しているのですが。

if ($0 ~ /大阪府 start/ && ...) のような形に修正したら
エラーは消えますか?
エラーのままでも、補足にメッセージそのまま貼り付けてください。

投稿日時 - 2006-09-16 00:06:54

お礼

ご回答ありがとうございます。
if文でエラーになる現象を再現できませんでした。
すいません。

ご提供頂いたスクリプトを実行すると以下のエラーが発生します。

awk: 構文エラー (1 行目の周辺)
awk: 処理を中止します。 (1 行目の周辺)

ご回答の程よろしくお願いします。

投稿日時 - 2006-09-19 21:50:55

ANo.7

No.4です。
/usr/bin/awk(/bin/awk)は-vオプションがないみたいなのでエラーになるようです。
少し修正して、Solaris 10(x86版)で実行してみました。

>(1)ファイルから"天王寺支店"が何行目にあるか調べる
>(2)(1)の結果の行数から上下に検索を行い、"大阪府 start"と"大阪府 end"が直近の何行目にあるか調べる。

に該当する部分がawkの所。
考え方は、No.5の方が書いた通り。

>(3)(2)の結果の行数を指定して"天王寺支店"の文字を含む"大阪府 start~大阪府 end"のくくりを抜き出す

の部分がsedの部分。

ちなみに↓のスクリプトですが動作すればいいや程度のものですので。

% uname -a
SunOS Solaris10 5.10 Generic_118855-14 i86pc i386 i86pc

% cat data
大阪府 start
淀川支店
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
10000円
大阪府 end
大阪府 start
天王寺支店
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
1000円
大阪府 end
大阪府 start
住吉支店
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
DDDDDDDDDDDDDDDDDDD
0円
大阪府 end
大阪府 start
天王寺支店
EEEEEEEEEEEEEEEEEEE
FFFFFFFFFFFFFFFFFFF
1500円
大阪府 end

% cat sample.sh
#!/bin/sh

branch="$1"

datafile=data

linelist=`awk '
BEGIN{Flag=0}
/start$/{Start=NR}
/'${branch}'/{Flag=1}
/end$/{if(Flag){printf("%d,%d ",Start,NR);Flag=0}}' ${datafile}`

for opt in ${linelist}
do
sed -n ${opt}p ${datafile}
done

% ./sample.sh 天王寺支店
大阪府 start
天王寺支店
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
1000円
大阪府 end
大阪府 start
天王寺支店
EEEEEEEEEEEEEEEEEEE
FFFFFFFFFFFFFFFFFFF
1500円
大阪府 end

投稿日時 - 2006-09-15 20:53:38

補足

すいません。
再び質問させて下さい。
以下の「;Flag=0」はどういう意味なのでしょうか?

{if(Flag){printf("%d,%d ",Start,NR);Flag=0}}

投稿日時 - 2006-09-21 09:58:46

お礼

ご回答ありがとうございます。

希望結果が出力出来そうです。

ありがとうございました。

投稿日時 - 2006-09-20 21:38:42

ANo.6

awkだけで片付けてみました。
start と end の行も出力したいのなら、コメントにしてある行を
使うようにしてください。

#!/usr/bin/awk
/^大阪府 start$/, /^大阪府 end$/ {
  if (/^大阪府 start$/)
    cnt=0

  data[cnt++] = $0

  if (/^大阪府 end$/ && exit_flag==1)
    exit(0)
  if (/^天王寺支店$/)
    exit_flag = 1
}
END {
  #for (i=0; i<cnt; i++)
  for (i=1; i<cnt-1; i++)
    printf "%2d:%s\n", i, data[i]
}

支店のデータが巨大で配列に収められない場合でもできますが
それはまたのお楽しみに。

POSIXのawkなら途中でexitしてもENDブロックを実行するはずですが、
ひょっとしたらSolarisのawkはそうでないかも?

そのときは適当にフラグを見て読み飛ばすなどしてください。

投稿日時 - 2006-09-15 00:30:00

お礼

ご回答ありがとうございます。
本スクリプトを実行するとif文のところでエラーとなってしまいます。
何故でしょうか?

投稿日時 - 2006-09-15 20:03:24

ANo.5

天王寺支店の上下に大阪府startと大阪府endが必ずあるということでしたら、
先頭から順に読んでいき
1.大阪府startの行を記憶する。
新しい大阪府startがでたら、次々と置き換える。
2.天王寺支店が、出現したら、
最後の大阪府startの行を採用する。
3.以降は、大阪府endを探して終わり。
でどうでしょうか。

投稿日時 - 2006-09-14 21:42:39

ANo.3

えぇと....
「下記例でいくと「大阪府 end」の行数から「大阪府 start」を検索し、その行数を取得するということをやりたいです。」
という文章の意味がわかりません.
挙げられたデータに対して, どのような出力がほしいのかをもっとわかりやすく (できれば具体的に) 示していただけませんか?

投稿日時 - 2006-09-14 13:13:15

お礼

ご確認ありがとうございます。
例をちょっと変えてみました。
(ファイルの中身には「大阪府 start~大阪府 end」という一定の法則性があります。
しかし、このくくりの中の内容はそれぞれ異なります。支店名(重複無し)が違ったり、レコード数が違ったり)

やりたいことは「"天王寺支店"の文字を含む"大阪府 start~大阪府 end"のくくりを抜き出す」
ということです。
自分なりに考えた抜き出し方はこうです。

 (1)ファイルから"天王寺支店"が何行目にあるか調べる
 (2)(1)の結果の行数から上下に検索を行い、"大阪府 start"と"大阪府 end"が直近の何行目にあるか調べる。
 (3)(2)の結果の行数を指定して"天王寺支店"の文字を含む"大阪府 start~大阪府 end"のくくりを抜き出す

上記(2)の方法がどうしても分かりません。awkで出来れば最高なのですが。

<例>
大阪府 start
淀川支店
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
10000円
大阪府 end
大阪府 start
天王寺支店
CCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDD
1000円
大阪府 end
大阪府 start
住吉支店
AAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBB
DDDDDDDDDDDDDDDDDDD
0円
大阪府 end

以上、よろしくお願いします。

投稿日時 - 2006-09-14 17:15:17

ANo.2

http://oshiete1.goo.ne.jp/kotaeru.php3?q=2348178

↑で質問した内容と同じように思えるのですが、その内容と今回は違うという事ですか?

あと、回答がありましたが、その時の質問の回答としては希望の事は出来なかったという事でしょうか?

投稿日時 - 2006-09-13 22:17:48

お礼

ご認識の通りです。
上記にある
(下記例でいくと「大阪府 end」の行数から「大阪府 start」を検索し、
その行数を取得するということをやりたいです。)
ということがやりたいのですが分かりません。

投稿日時 - 2006-09-14 09:27:54

ANo.1

AWKしばらくやってないから、自信ないのですが…。

行数を調べるのでしょうか? それとも、
単にその部分を抽出すればいいのでしょうか?

抽出すればいいだけだったら、
下記のような感じのコードで行けるのではないかと思います。

BEGIN{
flag = 0;
Country = "大阪府";
}
$1~Country && $2~"start"{
flag = 1;
}
{
if(flag == 1)print;
}
$1 ~ Country && $2 ~ "end"{
flag = 0;
}

投稿日時 - 2006-09-13 21:28:32

お礼

ご回答ありがとうございます。
最終的には抽出をするのですが、その過程で該当行数を調べたいです。

上記の「「大阪府 end」の行数から「大阪府 start」を検索し、
その行数を取得するということをやりたいです。」ということをやりたいのですが可能でしょうか?
awkで指定した行数から上方向への検索というのは可能なのでしょうか?

投稿日時 - 2006-09-14 09:30:39

あなたにオススメの質問