web-dev-qa-db-ja.com

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

すべての文字の後に余分なスペースが追加された大量のテキストがあるテキストドキュメントがあります。

例:

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…

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

awkまたはsedを取得して余分なスペースを削除する方法はありますか? (残念ながら、このテキストドキュメントは膨大であり、手動で処理するには非常に長い時間がかかります。) 何らかのテキスト認識も必要になるため、これはおそらく単純なbashスクリプトで解決するにははるかに複雑な問題であることを理解しています。

この問題にどのように対処できますか?

12
lloowen

次の正規表現は、スペース文字列の最初のスペースを削除します。それでうまくいくはずです。

s/ ( *)/\1/g

だから次のようなもの:

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

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

16
Dewi Morgan

純粋なPython WordセグメンテーションNLPパッケージ wordsegment を使用します。

$ 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
17
Lynn

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

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'

...出力:

本はより重要な分析目的も持っています

13
Julie Pelletier

Perlが助けに!

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

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

#!/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の可能な読みを生成します。

10
choroba

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

次のような入力で:

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が見つけた専門ライブラリ のようです

6

Dewi Morganのバージョンに似ていますが、sedが含まれています。

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

これはPerlの1ライナーを使用して実行できます(そして実行する必要があります)が、小さな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
deamentiaemundi

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

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

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

1
Donagh McCarthy

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
user189465
$ 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
0
Kaz