Script yang menghapus spasi ekstra antara huruf dalam teks

12

Saya memiliki dokumen teks yang memiliki banyak teks yang memiliki ruang tambahan ditambahkan setelah setiap huruf!

Contoh:

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

Secara visual:

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…

Perhatikan bahwa ada spasi tambahan setelah setiap huruf, jadi ada dua spasi di antara kata-kata yang berurutan.

Apakah ada cara saya bisa mendapatkan awkatau sedmenghapus ruang ekstra? (Sayangnya dokumen teks ini sangat besar dan akan memakan waktu yang sangat lama untuk dilakukan secara manual.)  Saya menghargai bahwa ini mungkin masalah yang jauh lebih kompleks untuk diselesaikan hanya dengan skrip bash sederhana karena harus ada semacam pengenalan teks juga.

Bagaimana saya bisa mendekati masalah ini?

rendah hati
sumber
2
itu sepele untuk mengganti semua spasi dengan apa-apa .. tapi saya pikir Anda ingin memisahkan kata-kata?
Sundeep
misalnya:echo 't h i s i s a n e x a m p l e' | sed 's/ //g'
Sundeep
1
Itu tidak membatasi perubahan ruang antar huruf . (Digit dan tanda baca bukan huruf , misalnya). Anda dapat melakukan ini dengan sed loop. Ini juga mungkin merupakan duplikat.
Thomas Dickey
1
untuk membatasi hanya di antara huruf: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: Sumber revisi asli menunjukkan bahwa spasi di antara kata-kata digandakan. Mengapa Anda membatalkan penggandaan di suntingan?
El'endia Starman

Jawaban:

16

Regex berikut akan menghapus spasi pertama dalam string spasi apa pun. Itu harus melakukan pekerjaan.

s/ ( *)/\1/g

Jadi sesuatu seperti:

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

... akan mengganti infile.txt dengan versi "tetap".

Dewi Morgan
sumber
@terdon Saya perhatikan dalam beberapa waktu terakhir bahwa orang-orang telah berhenti menulis skrip perl pie perl -pie- seperti yang ditunjukkan oleh hasil edit Anda. Apa alasannya? -Pie selalu bekerja dengan baik untuk saya, dan merupakan mnemonik yang hebat. Apakah perilaku saya berubah untuk memperlakukan apa pun yang mengikuti sebagai ekstensi, bukan hanya hal-hal yang dimulai dengan titik? Tampaknya aneh bagi mereka untuk memecahkan sesuatu yang begitu idiomatis.
Dewi Morgan
1
Hah, yah itu bukan idiom yang saya kenal. Perl telah seperti ini selama saya telah menggunakan-i . Di sisi lain, saya hanya pernah menggunakannya di mesin Linux dan saya belum mengetahuinya selama lebih dari beberapa tahun, jadi saya tidak dapat berbicara tentang perilaku yang lebih lama. Pada meskipun mesin saya, ini: perl -pie 's/a/b/' f, menghasilkan kesalahan: Can't open perl script "s/o/A/": No such file or directory. Sementara perl -i -pe 's/o/A/' fbekerja seperti yang diharapkan. Jadi ya, eini diambil sebagai ekstensi cadangan.
terdon
Muka sedih. Ah, well, waktu terus berjalan, dan itu artinya saya perlu mempelajari kembali urutan parameter. Kurasa otakku licin. Terima kasih telah memberi tahu saya, dan untuk memperbaiki kode saya!
Dewi Morgan
17

Gunakan wordsegment, paket NLP segmentasi kata Python murni:

$ 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
Lynn
sumber
1
Menggunakan NLP mungkin merupakan solusi yang paling efektif jika tidak ada hal lain untuk membedakan kata-kata. NLP berkinerja lebih baik daripada kamus lihat-depan dalam banyak kasus.
grochmal
13

Berdasarkan fakta bahwa input mencakup spasi ganda antar kata, ada solusi yang lebih sederhana. Anda cukup mengubah spasi ganda menjadi karakter yang tidak digunakan, menghapus spasi dan mengubah karakter yang tidak digunakan kembali ke spasi:

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'

... keluaran:

Buku ini juga memiliki tujuan analitis yang lebih penting

Julie Pelletier
sumber
5
Perintah sed dengan makna "ganti setiap kemunculan karakter non-spasi, diikuti spasi dengan karakter non-spasi yang sesuai" melakukan hal yang sama:sed -e "s/\([^ ]\) /\1/g"
woodengod
3
Itu memang alternatif yang baik. Anda harus mempostingnya sebagai jawaban untuk mendapatkan kredit untuk itu.
Julie Pelletier
10

Perl untuk menyelamatkan!

Anda memerlukan kamus, yaitu file yang mencantumkan satu kata per baris. Di sistem saya, itu ada sebagai /var/lib/dict/words, saya juga melihat file yang sama seperti /usr/share/dict/britishdll.

Pertama, Anda mengingat semua kata dari kamus. Kemudian, Anda membaca baris input demi baris, dan mencoba menambahkan karakter ke sebuah kata. Jika mungkin, Anda ingat kata itu dan mencoba menganalisis sisa kalimat. Jika Anda mencapai ujung garis, Anda menampilkan garis.

#!/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;
}

Untuk masukan Anda, ini menghasilkan 4092 kemungkinan bacaan di sistem saya.

choroba
sumber
gagal tes dengan versi spaced out a cat a logyaitua c a t a l o g
ctrl-alt-delor
@ Richard: OBOE, diperbaiki. Tapi sekarang menghasilkan terlalu banyak kemungkinan, cobalah untuk menghapus satu kata kata.
choroba
@richard Anda dapat mengatasi masalah ini dengan bantuan algoritma non-deterministik (mis. semua bacaan yang mungkin disimpan) dan menerapkan pengurai di atasnya Kemudian Anda bisa memfilter semua 4000 bacaan yang mungkin ke bacaan tunggal dengan jumlah kesalahan paling sedikit.
bash0r
6

Catatan: jawaban ini (seperti beberapa yang lain di sini) didasarkan pada versi sebelumnya dari pertanyaan di mana kata-kata tidak dibatasi. Versi yang lebih baru bisa dijawab sepele .

Pada input seperti:

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

Kamu bisa mencoba:

 $ 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

Ia memproses dari kiri ke kanan dan menemukan satu kata terpanjang setelah kata berikutnya.

Jelas, di sini, ini bukan pilihan kata yang terbaik karena kalimat itu tidak masuk akal, tetapi untuk membuat yang tepat, Anda memerlukan alat yang dapat memahami tata bahasa atau makna teks atau setidaknya beberapa statistik informasi tentang kata-kata apa yang mungkin ditemukan bersama untuk menghasilkan kumpulan kata yang paling mungkin. Sepertinya solusinya adalah perpustakaan khusus seperti yang ditemukan oleh Lynn

Stéphane Chazelas
sumber
@terdon, lihat edit. Masalahnya adalah bahwa pertanyaan itu diubah dari yang kompleks dan menarik menjadi yang sepele. Apakah ada cara Anda dapat membaginya menjadi dua pertanyaan sebelum dan sesudah pengeditan?
Stéphane Chazelas
Aku takut tidak, tidak. Tapi tetap saja trik yang cerdas, meskipun tidak sempurna.
terdon
1
Sebenarnya, pertanyaan itu sepele dari awal - lihat versi pertama dan sumbernya . Sayangnya, OP tidak mengerti bagaimana Stack Exchange menerjemahkan teks, sehingga teks input yang benar tidak terlihat sampai trichoplax memperbaiki format - dan, yang lebih sayangnya, itu tidak terlihat saat itu , karena orang yang menyetujui pengeditan itu segera pergi dan memecahkannya.
Scott
2

Mirip dengan versi Dewi Morgan, tetapi dengan sed:

$ echo "f o o  t h e  b a r" | sed -r "s/[ ]{1}([^ ]{1})/\1/g"
foo the bar
Jaleks
sumber
Itu sedhanya GNU dan itu tidak setara dengan Dewi. sedSetara standar dari Dewi adalahsed 's/ \( *\)/\1/g'
Stéphane Chazelas
perhatikan "serupa" ;-)
Jaleks
1

Meskipun bisa (dan harus) dilakukan dengan Perl one-liner, parser C kecil juga akan sangat cepat, dan juga sangat kecil (dan mudah-mudahan sangat benar):

#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);
}

Disusun dengan

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

(programnya sedikit kurang dari 9kb)

Gunakan dalam pipa seperti misalnya:

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
deamentiaemundi
sumber
1

Saya mencoba ini dan sepertinya berhasil:

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

The sedperintah menangkap dua kelompok dan kembali hanya yang pertama.

Donagh McCarthy
sumber
0

Dalam c ++, saya akan melakukan ini:

#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;
    }

Akan mengubah isi file teks uji, menjadi string yang sama, tetapi dengan spasi antara huruf dihapus. (Ini membutuhkan ruang antara setiap huruf untuk menjadi akurat).

pengguna189465
sumber
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
Kaz
sumber