テキスト内の文字間の余分なスペースを削除するスクリプト


12

私はすべての文字の後に追加のスペースが追加されたテキストの負荷があるテキスト文書を持っています!

例:

T h e  b o o k  a l s o  h a s  a n  a n a l y t i c a l  p u r p o s e  w h i c h  i s  m o r e  i m p o r t a n t

視覚的に:

T␣h␣e␣␣b␣o␣o␣k␣␣a␣l␣s␣o␣␣h␣a␣s␣␣a␣n␣␣a␣n␣a␣l␣y␣t␣i c␣a␣l␣␣p␣u␣r␣p␣o␣s␣e␣␣w␣h␣i␣c␣h␣␣i␣s␣␣m␣o␣r␣e␣␣i␣ m␣p␣o␣r␣t␣a␣n␣t…

余分があることに注意してください各文字の後にスペースため、連続する単語の間に2つのスペース。

私が得ることができる方法はありますか awksed余分なスペースをまたは削除ますか?(残念ながら、このテキスト文書は膨大であり、手動で確認するには非常に長い時間がかかります。)  何らかのテキスト認識も必要であるため、これはおそらく単純なbashスクリプトで解決するはるかに複雑な問題であることを感謝します。

この問題にどのようにアプローチできますか?


2
すべてのスペースをゼロに置き換えるのは簡単です。しかし、言葉を分けたいと思いますか?
サンディープ

例:echo 't h i s i s a n e x a m p l e' | sed 's/ //g'
Sundeep

1
それは文字の間のスペースへの変更を制限しません。(たとえば、数字と句読点は文字ではありません)。ループを使用してsedでこれを行うことができます。これもおそらく重複しています。
トーマスディッキー

1
文字間のみを制限するには:echo 'T h i s ; i s .a n 9 8 e x a m p l e' | perl -pe 's/[a-z]\K (?=[a-z])//ig'
Sundeep

4
@JuliePelletier:元のリビジョンのソースは、単語間のスペースが2倍になったことを示しています。編集でそれらを二重にしなかったのはなぜですか?
エレンディアスターマン

回答:


16

次の正規表現は、スペースの文字列の最初のスペースを削除します。それは仕事をする必要があります。

s/ ( *)/\1/g

のようなもの:

perl -i -pe 's/ ( *)/\1/g' infile.txt

... infile.txtを「修正済み」バージョンに置き換えます。


@terdon私は最近perl -pie、あなたの編集が示すように、人々がperl pieスクリプトを書くのをやめることに気付きました。この理由は何ですか?-pieは常に私にとってうまく機能し、素晴らしいニーモニックです。-iの動作は、ドットで始まるものだけでなく、後に続くものを拡張子として扱うように変更されましたか?彼らがとても慣用的なものを壊すのは奇妙に思えます。
デウィモーガン

1
ええと、それは私がよく知っているイディオムではありません。Perlは、私が使用している限り、このようにしてきました-i。一方、私はこれをLinuxマシンでしか使用したことがなく、数年以上それについて知らなかったため、その古い動作について話すことはできません。しかし、私のマシンでは、this:がperl -pie 's/a/b/' fエラーを生成します:Can't open perl script "s/o/A/": No such file or directory。一方でperl -i -pe 's/o/A/' f作品の予想通り。はい、これeはバックアップ拡張機能として使用されます。
テルドン

悲しい顔。ああ、まあ、時間が進みます。それは、単にパラメーターの順序を再学習する必要があることを意味します。私の脳はぐにゃぐにゃしていると思う。私に知らせてくれて、私のコードを修正してくれてありがとう!
デウィモーガン

17

使用wordsegment、純粋なPythonの単語分割NLPパッケージ:

$ pip install wordsegment
$ python2.7 -m wordsegment <<<"T h e b o o k a l s o h a s a n a n a l y t i c a l p u r p o s e w h i c h i s m o r e i m p o r t a n t"
the book also has an analytical purpose which is more important

1
NLPを使用することは、言葉を区別するものが他にない場合、おそらく最も効果的なソリューションです。ほとんどの場合、NLPは先読み辞書よりも優れたパフォーマンスを発揮します。
-grochmal

13

入力に単語間の二重スペースが含まれるという事実に基づいて、はるかに簡単な解決策があります。ダブルスペースを未使用の文字に変更し、スペースを削除して、未使用の文字をスペースに戻すだけです。

echo "T h e  b o o k  a l s o  h a s  a n  a n a l y t i c a l  p u r p o s e  w h i c h  i s  m o r e  i m p o r t a n t  " | sed 's/  /\-/g;s/ //g;s/\-/ /g'

...出力:

本には、より重要な分析目的もあります


5
「ちょうど対応する非空白文字とスペースが続く非空白文字のすべての発生を置き換え、」意味を持つのsedコマンドでは、同じことを:sed -e "s/\([^ ]\) /\1/g"
woodengod

3
それは確かに良い選択肢です。あなたはそれの信用を得るために答えとしてそれを投稿すべきです。
ジュリーペレティエ

10

Perlが助けになります!

辞書が必要です。つまり、1行に1つの単語をリストしたファイルです。私のシステムでは、として存在し/var/lib/dict/words、同様のファイルも見ました/usr/share/dict/britishなど。

まず、辞書のすべての単語を覚えています。次に、入力を行ごとに読み取り、単語に文字を追加しようとします。可能であれば、その単語を覚えて、残りの行を分析してみてください。行の終わりに到達すると、行を出力します。

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

my $words = '/var/lib/dict/words';
my %word;

sub analyze {
    my ($chars, $words, $pos) = @_;
    if ($pos == @$chars) {
        $_[3] = 1;  # Found.
        say "@$words";
        return
    }
    for my $to ($pos .. $#$chars) {
        my $try = join q(), @$chars[ $pos .. $to ];
        if (exists $word{$try}) {
            analyze($chars, [ @$words, $try ], $to + 1, $_[3]);
        }
    }
}


open my $WORDS, '<', $words or die $!;
undef @word{ map { chomp; lc $_ } <$WORDS> };

while (<>) {
    my @chars = map lc, /\S/g;
    analyze(\@chars, [], 0, my $found = 0);
    warn "Unknown: $_" unless $found;
}

入力のために、私のシステムで4092の可能な読み取り値を生成します。


間隔をあけバージョンでテストを失敗したa cat a logすなわちa c a t a l o g
Ctrl + Alt + delor

@richard:OBOE、修正済み。しかし、現在では生成される可能性が多すぎるため、1文字の単語を削除してください。
チョロバ

@richard非決定的アルゴリズムの助けを借りてこの問題と戦うことができ(たとえば、すべての可能な読み取り値が保存される)、それにパーサーを適用します。次に、4000個すべての可能な読み取り値を、エラー数が最小の単一の読み取り値にフィルターできます。
bash0r

6

注:この回答(ここにある他のいくつかの回答と同様)は、単語が区切られていない以前のバージョンの質問に基づいています。新しいバージョンは簡単に回答できます。

次のような入力で:

T h e b o o k a l s o h a s a n a n a l y t i c a l p u r p o s e w h i c h i s m o r e i m p o r t a n t

あなたが試すことができます:

 $ tr -d ' ' < file | grep -oiFf /usr/share/dict/words | paste -sd ' '
 The book also has ana na l y tic al purpose which ism ore important

左から右に処理し、次の単語から最も長い単語を見つけます。

明らかに、ここでは、その文は意味をなさないため、単語の最良の選択ではありませんが、正しい文を見つけるには、テキストの文法や意味、または少なくとも統計的なものを理解できるツールが必要です最もありそうな単語のセットを見つけるために、どの単語が一緒に見つかる可能性が高いかに関する情報。ソリューションは、Lynnが見つけ専門のライブラリのようです


@terdon、編集を参照してください。問題は、その質問が複雑で興味深いものから些細なものに変更されたことです。編集前と編集後の2つの質問に分割する方法はありますか?
ステファンシャゼラス

怖くない、いや。完璧ではないにしても、まだ賢いトリックです。
テルドン

1
厳密に言えば、質問は最初から些細なものでした。最初のバージョンそのソースを参照してください。まで、正しい入力テキストが見えなかったので、残念ながら、OPは、スタックExchangeは、テキストをレンダリングする方法を理解していなかったセンモウヒラムシは、 書式設定を固定して、さらに残念なことに、それは見えなかった- その後、すぐにその編集を承認したので、人、行って壊した。
スコット

2

Dewi Morganのバージョンに似ていますが、sedを使用しています。

$ echo "f o o  t h e  b a r" | sed -r "s/[ ]{1}([^ ]{1})/\1/g"
foo the bar

これはGNU sedのみであり、Dewiのものと同等ではありません。標準sedデウィさんの同等のようになりますsed 's/ \( *\)/\1/g'
ステファンChazelas

「類似」に注意してください;-)
Jaleks

1

Perlのワンライナーを使用して実行できます(実行する必要があります)が、小さなCパーサーも非常に高速で、非常に小さい(そして、うまくいけば非常に正確です):

#include <stdio.h>
#include <stdlib.h>

int main()
{
  char c1 = '\0', c2 = '\0', tmp_c;

  c1 = fgetc(stdin);
  for (;;) {
    if (c1 == EOF) {
      break;
    }
    c2 = fgetc(stdin);
    if (c2 == EOF) {
      if (c1 != ' ') {
        fputc(c1, stdout);
      }
      break;
    }
    if (c1 == c2 && c1 == ' ') {
      tmp_c = fgetc(stdin);
      if (tmp_c != EOF) {
        if (tmp_c != '\n') {
          ungetc(tmp_c, stdin);
          fputc(' ', stdout);
        } else {
          ungetc(tmp_c, stdin);
        }
      } else {
        break;
      }
    } else if (c1 != ' ') {
      fputc(c1, stdout);
    }
    c1 = c2;
  }
  exit(EXIT_SUCCESS);
}

コンパイル済み

gcc-4.9 -O3 -g3  -W -Wall -Wextra -std=c11 lilcparser.c -o lilcparser

(プログラムは9kb未満です)

次のようなパイプで使用します。

echo "T h e  b o o k  a l s o  h a s  a n  a n a l y t i c a l  p u r p o s e  w h i c h  i s  m o r e  i m p o r t a n t  " | ./lilcparser

1

私はこれを試しましたが、うまくいくようです:

echo "<text here>" | sed -r 's/(\w)(\s)/\1/g'

このsedコマンドは2つのグループをキャプチャし、最初のグループのみを返します。


0

C ++では、これを行います。

#include <fstream>
using namespace std;

int main()
{   
    fstream is("test.txt", std::ios::in);

    char buff;
    vector<char>str;

    while (!is.eof()){is.get(buff);str.push_back(buff);} //read file to string

    for (int a=0;a<str.size();++a)if (str[a] == ' ' && str[a + 1] != ' ')str.erase(str.begin()+a);
    is.close();

    ofstream os("test.txt", std::ios::out | std::ios::trunc); //clear file for rewrite

    os.write(str.data(), str.size() * sizeof(char)); //write chars
    os.close();

    return 0;
    }

テストテキストファイルの内容を同じ文字列に変更しますが、文字間のスペースは削除されます。(正確にするためには、すべての文字の間にスペースが必要です)。


0
$ echo 'F o u r  s c o r e  a n d' | \
txr -t '(mapcar* (opip (split-str @1 "  ")
                       (mapcar (op regsub #/ / ""))
                       (cat-str @1 " "))
                 (get-lines))'
Four score and


$ txr -e '(awk (:begin (set fs "  "))
               ((mf (regsub #/ / ""))))'  # mf: modify fields
F o u r  s c o r e  a n d
Four score and


$ awk -F'  ' '{for(i=1;i<=NF;i++)gsub(/ /,"",$i);print}'
F o u r  s c o r e  a n d
Four score and
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.