スペースを含む行の長さでテキストファイルを並べ替える


137

次のようなCSVファイルがあります

AS2345、ASDF1232、Mr. Plain Example、110 Binary ave。、Atlantis、RI、12345、(999)123-5555,1.56
AS2345、ASDF1232、Mrs。Plain Example、1121110 Ternary st。110 Binary ave ..、Atlantis、RI、12345、(999)123-5555,1.56
AS2345、ASDF1232、Mr. Plain Example、110 Binary ave。、RI、リバティシティ、12345、(999)123-5555,1.56
AS2345、ASDF1232、Mr. Plain Example、110 Ternary ave。、Some City、RI、12345、(999)123-5555,1.56

スペースを含む行の長さで並べ替える必要があります。次のコマンドにはスペースが含まれていません。それを変更して自分で機能するようにする方法はありますか?

cat $@ | awk '{ print length, $0 }' | sort -n | awk '{$1=""; print $0}'

21
私は実際にそれらの人々は確かに「8192のようなもので同意するだろう、バイナリ・アベニューや三元ストリートに住んでしたいですラウンド数」
schnaader

回答:


224

回答

cat testfile | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-

または、同じ長さの行の元の(おそらく意図しない)サブソートを行うには、次のようにします。

cat testfile | awk '{ print length, $0 }' | sort -n | cut -d" " -f2-

どちらの場合も、最終的なカットのためにawkから離れることで、指定された問題を解決しました。

長さが一致する線-引き分けの場合の処理​​:

質問では、長さが一致する行に対してさらにソートが必要かどうかは指定されていませんでした。これは望ましくないと想定し、-s--stable)を使用して、このような行が互いに並べ替えられないようにし、入力で発生する相対的な順序でそれらを維持することを提案しました。

(これらのタイのソートをより細かく制御したい人は、ソートの--keyオプションを検討するかもしれません。)

質問の解決策が失敗する理由(awk行の再構築):

以下の違いに注意してください。

echo "hello   awk   world" | awk '{print}'
echo "hello   awk   world" | awk '{$1="hello"; print}'

彼らはそれぞれ降ります

hello   awk   world
hello awk world

(gawkの)マニュアル関連セクションでは、1つのフィールドを変更すると、awkが$ 0全体(セパレーターなどに基づいて)を再構築することを余談として述べています。私はそれがクレイジーな振る舞いではないと思います。これには:

「最後に、フィールドとOFSの現在の値を使用して、awkにレコード全体を強制的に再構築させると便利な場合があります。これを行うには、一見無害な割り当てを使用します。」

 $1 = $1   # force record to be reconstituted
 print $0  # or whatever else with $0

「これにより、awkはレコードを再構築する必要があります。」

同じ長さのいくつかの行を含むテスト入力:

aa A line   with     MORE    spaces
bb The very longest line in the file
ccb
9   dd equal len.  Orig pos = 1
500 dd equal len.  Orig pos = 2
ccz
cca
ee A line with  some       spaces
1   dd equal len.  Orig pos = 3
ff
5   dd equal len.  Orig pos = 4
g

1
heemayl、そうです、ありがとう。私はOPが試みた解決策の形を可能な限り一致させ、彼と私の重要な違いのみに集中できるようにしました。
neillb 2017年

1
cat $@壊れていることも指摘する価値があります。あなたは間違いなくそれを引用したいと思いますcat "$@"
例えば

27

neillbAWKソリューションは、本当に使いたい場合に最適でawkあり、なぜそれが面倒なのかを説明しますが、やりたいことをすばやく行い、何をすべきかを気にしない場合は、1つのソリューションを使用しますsort()入力行を反復するカスタムcaparisonルーチンを備えたPerlの関数。ここにワンライナーがあります:

perl -e 'print sort { length($a) <=> length($b) } <>'

これを必要な場所にパイプラインに配置して、(からcatまたはシェルリダイレクトから)STDINを受信するか、別の引数としてperlにファイル名を指定してファイルを開くことができます。

私はスワップアウトして私の場合は、最初に最も長い行を必要$a$b比較して。


これは、入力ファイルに数値行と英数字行が含まれている場合、awkが予期しないソートを引き起こすため、より良い解決策です。ここで、1行コマンド:$ cat testfile | perl -e 'print sort {length($ a)<=> length($ b)} <>'
alemol

速い!出力が別のファイルにリダイレクトされたとき、1秒未満で465,000行のファイル(1行あたり1ワード)cat testfile.txt | perl -e 'print sort { length($a) <=> length($b) } <>' > out.txt
でした

StrawberryPerlを使用したWindowsは動作します:type testfile.txt | perl -e "print sort { length($a) <=> length($b) } <>" > out.txt
bryc

14

代わりにこのコマンドを試してください:

awk '{print length, $0}' your-file | sort -n | cut -d " " -f2-

10

ベンチマーク結果

以下は、この質問に対する他の回答からのソリューション全体のベンチマークの結果です。

試験方法

  • 高速マシンでの10回の連続実行、平均
  • Perl 5.24
  • awk 3.1.5(gawk 4.1.0の時間は最大2%高速でした)
  • 入力ファイルは550MB、600万行の怪物です(British National Corpus txt)

結果

  1. Calebのperlソリューションは11.2秒かかりました
  2. 私のperlソリューションは11.6秒かかりました
  3. neillbのawkソリューション#1は20秒かかりました
  4. neillbのawkソリューション#2は23秒かかりました
  5. anubhavaのawkソリューションは24秒かかりました
  6. ジョナサンのawkソリューションは25秒かかりました
  7. フレッツのbashソリューションは、awkソリューションよりも400倍長くかかります(1万行の切り詰められたテストケースを使用)。それはうまくいきます、ただ永遠にかかります。

追加perlオプション

また、別のPerlソリューションを追加しました。

perl -ne 'push @a, $_; END{ print sort { length $a <=> length $b } @a }' file

6

ピュアバッシュ:

declare -a sorted

while read line; do
  if [ -z "${sorted[${#line}]}" ] ; then          # does line length already exist?
    sorted[${#line}]="$line"                      # element for new length
  else
    sorted[${#line}]="${sorted[${#line}]}\n$line" # append to lines with equal length
  fi
done < data.csv

for key in ${!sorted[*]}; do                      # iterate over existing indices
  echo -e "${sorted[$key]}"                       # echo lines with equal length
done

3

length()この関数は、スペースを含んでいます。パイプラインを少し調整します(UUOCの回避を含む)。

awk '{ printf "%d:%s\n", length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]*://'

このsedコマンドは、awkコマンドによって追加された数字とコロンを直接削除します。または、次のようにフォーマットを維持しますawk

awk '{ print length($0), $0;}' "$@" | sort -n | sed 's/^[0-9]* //'

2

これらの解決策は、ファイルに数字で始まる行が含まれている場合は機能しないことを発見しました。なぜなら、それらはすべてのカウントされた行とともに数値でソートされるからです。解決策はsort-g(数値ソート)の代わりに-n(一般数値ソート)フラグを与えることです:

awk '{ print length, $0 }' lines.txt | sort -g | cut -d" " -f2-

2
こんにちは、マーカス。行の長さではなく、行の内容(数値かどうかにかかわらず)を確認していません。長さが一致する行の場合を除いて、並べ替えに影響があるためです。これはあなたが意味したことですか?このような場合、改善方法を-n提案-gするソート方法から提案された方法への切り替えが見つからなかったので、期待していません。これで、私の回答で、長さが等しい行のサブソートを禁止する方法について説明しました(を使用--stable)。それがあなたの意図したことであるかどうかにかかわらず、私の注意を喚起してくれてありがとう!また、テスト用に考慮された入力を追加しました。
neillb

4
いいえ、分解して説明します。そのawk部分だけで、行の長さとスペースが前に付いた行のリストが生成されます。配管するとsort -n期待どおりに機能します。ただし、これらの行のいずれかがすでに先頭に番号がある場合、それらの行は長さ+スペース+番号で始まります。sort -nそのスペースは無視され、長さ+数値から連結された1つの数値として扱われます。-gフラグを使用すると、代わりに最初のスペースで停止し、正しい並べ替えが行われます。いくつかの数字の接頭辞が付いたファイルを作成して自分で試して、コマンドを段階的に実行します。
Markus Amalthea Magnuson 2016年

1
またsort -n、スペースが無視され、不適切な並べ替えが行われることもわかりました。sort -g正しい順序を出力します。
Robert Smith

私はして説明した問題を再現することはできません-nの中でsort (GNU coreutils) 8.21infoドキュメントでは説明し-g、あなたがする必要がない場合ので、おそらくそれを使用していない、あまり効率的で、(それがフロートに番号を変換)潜在的にあまり正確と。
フィル、

nb documentation for -n: "数値で並べ替えます。数値は各行で始まり、オプションの空白、オプションの「-」記号、および場合によっては千の位の区切り文字で区切られたゼロ以上の数字で構成され、オプションで後に小数点文字とゼロ以上の数字が続きます。空の数値は「0」として扱われます。「LC_NUMERIC」ロケールは、小数点文字と3桁ごとの区切り文字を指定します。デフォルトでは、空白はスペースまたはタブですが、「LC_CTYPE」ロケールはこれを変更できます。
フィル、


2

1)純粋なawkソリューション。その場合、行の長さを1024以上にすることはできないとします。

猫のファイル名| awk 'BEGIN {分= 1024; s = "";} {l = length($ 0); if(l <min){min = l; s = $ 0;}} END {print s} '

2)すべての行が1ワードのみであると仮定して、1つのライナーbashソリューション。

LINES = $(cat filename); $ LINESのkの場合。printf "$ k"を実行します。エコー$ k | wc -L; 完了| ソート-k2 | 頭-n 1 | カット-d "" -f1


1

これは、長さで行をソートするマルチバイト互換の方法です。以下が必要です。

  1. wc -m あなたは利用可能です(macOSはそれを持っています)。
  2. 現在のロケールは、たとえばを設定することにより、マルチバイト文字をサポートしますLC_ALL=UTF-8。これは、.bash_profileで設定するか、次のコマンドの前に追加するだけで設定できます。
  3. testfile ロケールに一致する文字エンコード(UTF-8など)があります。

完全なコマンドは次のとおりです。

cat testfile | awk '{l=$0; gsub(/\047/, "\047\"\047\"\047", l); cmd=sprintf("echo \047%s\047 | wc -m", l); cmd | getline c; close(cmd); sub(/ */, "", c); { print c, $0 }}' | sort -ns | cut -d" " -f2-

パーツごとに説明:

  • l=$0; gsub(/\047/, "\047\"\047\"\047", l);←awk変数の各行のコピーを作成し、すべてlをダブルエスケープして'、行をシェルコマンドとして安全にエコーできるようにします(\0478進表記の単一引用符です)。
  • cmd=sprintf("echo \047%s\047 | wc -m", l);←これは実行するコマンドで、エスケープされた行をにエコーしwc -mます。
  • cmd | getline c;←コマンドを実行し、返される文字カウント値をawk変数にコピーしますc
  • close(cmd); ←シェルコマンドへのパイプを閉じて、1つのプロセスで開いているファイルの数がシステムの制限に達しないようにします。
  • sub(/ */, "", c);←は、によって返される文字カウント値から空白を取り除きwcます。
  • { print c, $0 } ←行の文字カウント値、スペース、および元の行を印刷します。
  • | sort -ns←行を(先頭に追加された文字カウント値によって)数値-n順に並べ替え()、安定した並べ替え順序を維持します(-s)。
  • | cut -d" " -f2- ←付加された文字カウント値を削除します。

行ごとにサブコマンドを実行する必要があるため、処理速度は遅い(高速のMacbook Proでは1秒あたり160行のみ)。

または、これだけを行うだけですgawk(バージョン3.1.5以降、gawkはマルチバイトに対応しています)。これにより大幅に高速化されます。awkからシェルコマンドを介して安全に行を渡すためにすべてのエスケープと二重引用符を実行するのは非常に困難ですが、これは、追加のソフトウェアをインストールする必要がないことがわかった唯一の方法です(gawkはデフォルトでは利用できません)マックOS)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.