Metin içindeki harfler arasında fazladan boşluk silen bir betik


12

Her harften sonra fazladan boşluk eklenmiş bir metin yükü olan bir metin belgesi var!

Misal:

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

Görme:

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 ...

Her harften sonra fazladan boşluk olduğuna dikkat edin , bu yüzden ardışık kelimeler arasında iki boşluk vardır.

Fazla boşlukları almamın awkveya silmemin bir yolu var mı sed? (Ne yazık ki bu metin belgesi çok büyük ve el ile geçmesi çok uzun sürüyor.)  Ben de metin tanıma bir tür olması gerektiği gibi bu basit bir bash betiği ile çözmek için muhtemelen çok daha karmaşık bir sorun olduğunu takdir ediyorum.

Bu soruna nasıl yaklaşabilirim?


2
tüm boşlukları hiçbir şeyle değiştirmek önemsizdir ... ama bence kelimeleri ayırmak ister misiniz?
Sundeep

örneğin:echo 't h i s i s a n e x a m p l e' | sed 's/ //g'
Sundeep

1
Bu, harfler arasındaki boşluklardaki değişikliği sınırlamaz . ( Örneğin rakamlar ve noktalama işaretleri harf değildir ). Bunu bir döngü ile sed'de yapabilirsiniz. Bu da muhtemelen bir kopya.
Thomas Dickey

1
sadece harfler arasında kısıtlama: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: Orijinal revizyonun kaynağı , kelimeler arasındaki boşlukların iki katına çıktığını gösteriyor. Düzenlemenizde neden onları iki katına çıkardınız?
El'endia Starman

Yanıtlar:


16

Aşağıdaki normal ifade, herhangi bir boşluk dizesindeki ilk boşluğu kaldıracaktır. Bu işi yapmalı.

s/ ( *)/\1/g

Yani şöyle bir şey:

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

... infile.txt dosyasını "sabit" bir sürümle değiştirecektir.


@terdon Son zamanlarda insanların perl pasta betikleri yazmayı bıraktıklarını fark ettim perl -pie- düzenlemenizin gösterdiği gibi. Bunun mantığı nedir? -Pie benim için her zaman iyi çalıştı ve harika bir anımsatıcı. -İ'nin davranışı, yalnızca bir nokta ile başlayan şeylerden ziyade bir uzantı olarak muamele görmek için değişti mi? Bu kadar deyimsel bir şeyi kırmaları garip görünüyor.
Dewi Morgan

1
Hah, bu aşina olduğum bir deyim değil. Perl kullandığım sürece bu şekilde oldu -i. Öte yandan, sadece Linux makinelerinde kullandım ve birkaç yıldan fazla bir süredir bilmiyorum, bu yüzden eski davranışları hakkında konuşamam. Benim makine olsa, bu: perl -pie 's/a/b/' fbir hata üretir: Can't open perl script "s/o/A/": No such file or directory. İken perl -i -pe 's/o/A/' feserlerin beklendiği gibi. Yani evet, eyedekleme uzantısı olarak alınır.
terdon

Üzgün ​​surat. Ah, zaman geçiyor ve bu sadece bir parametre sırasını yeniden öğrenmem gerektiği anlamına geliyor. Beynimi yumuşatıyor sanırım. Bana bildirdiğiniz ve kodumu düzelttiğiniz için teşekkürler!
Dewi Morgan

17

Kullanım wordsegment, saf-Python kelime segmentasyon NLP paketi:

$ 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
Kelimeleri ayıracak başka bir şey yoksa, NLP kullanmak muhtemelen en etkili çözümdür. NLP, çoğu durumda ileriye dönük bir sözlükten daha iyi performans gösterir.
grochmal

13

Girdinin kelimeler arasında çift boşluk içerdiği gerçeğine dayanarak, çok daha basit bir çözüm var. Çift boşlukları kullanılmayan bir karakterle değiştirir, boşlukları kaldırır ve kullanılmayan karakteri tekrar bir boşluğa değiştirirsiniz:

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'

...çıktılar:

Kitabın ayrıca daha önemli bir analitik amacı var


5
"Boşluk olmayan bir karakterin her oluşumunu ve ardından yalnızca boşluk olmayan karaktere sahip bir boşluğu değiştir" anlamına gelen bir sed komutu aynı şeyi yapar:sed -e "s/\([^ ]\) /\1/g"
woodengod

3
Bu gerçekten iyi bir alternatif. Kredi almak için bir cevap olarak göndermelisiniz.
Julie Pelletier

10

Kurtarmaya Perl!

Bir sözlüğe, yani her satırda bir kelime listeleyen bir dosyaya ihtiyacınız vardır. Sistemimde de var /var/lib/dict/words, benzer dosyaları da gördüm /usr/share/dict/britishvb.

İlk olarak, sözlükteki tüm kelimeleri hatırlarsınız. Ardından, girişi satır satır okur ve bir sözcüğe karakter eklemeye çalışırsınız. Mümkünse, kelimeyi hatırlar ve satırın geri kalanını analiz etmeye çalışırsınız. Çizginin sonuna ulaşırsanız, çizginin çıktısını alırsınız.

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

Girişiniz için, sistemimde 4092 olası okuma üretir.


a cat a logiea c a t a l o g
ctrl-alt-delor

@richard: OBOE, sabit. Ama şimdi çok fazla olasılık üretiyor, bir harfli kelimeyi kaldırmaya çalışın.
choroba

@richard Belirleyici olmayan bir algoritma (örneğin tüm olası okumalar saklanır) yardımıyla bu sorunla mücadele edebilir ve üzerine bir ayrıştırıcı uygulayabilirsiniz. Daha sonra 4000 olası okumayı en az hata sayısına sahip tek bir okumaya filtreleyebilirsiniz.
bash0r

6

Not: Bu cevap (burada birkaç kişi gibi), sorunun sözcüklerin sınırlandırılmadığı önceki bir sürümüne dayanmaktadır . Yeni sürüm önemsiz bir şekilde cevaplanabilir .

Gibi bir girişte:

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

Deneyebilirsiniz:

 $ 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

Soldan sağa işler ve bir sonrakinden sonra en uzun kelimeyi bulur.

Açıkçası, burada, cümlenin bir anlamı olmadığı için en iyi kelime seçimi değil, ancak doğru olanı bulmak için metnin dilbilgisini veya anlamını veya en azından bazı istatistiksel kelimeleri anlayabilecek araçlara ihtiyacınız olacak hangi kelimelerin birlikte bulunabileceğine dair en olası kelime grubunu bulmak için bilgiler. Çözüm Lynn tarafından bulunan özel bir kütüphane gibi görünüyor


@terdon, bkz. düzenleme. Sorun şu ki, bu soru karmaşık ve ilginç bir sorudan önemsiz bir soruya dönüştürüldü. Düzenlemeden önce ve sonra iki soruya bölmenin bir yolu var mı?
Stéphane Chazelas

Korkmuyorum, hayır. Mükemmel olmasa bile yine de akıllı bir numara.
terdon

1
Açıkçası, soru baştan itibaren önemsizdi - ilk versiyona ve kaynağına bakın . Stack Exchange metni nasıl oluşturduğunu Maalesef OP kadar doğru giriş metni görünür değildi, anlamadı placozoa biçimlendirme sabit - hatta daha maalesef ve, görünür değildi o zaman , çünkü hemen o düzenlemeyi onayladı kişi gitti ve kırdı.
Scott

2

Dewi Morgan'ın versiyonuna benzer, ancak sed ile:

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

Bu sedsadece GNU ve Dewi'ninkiyle eşdeğer değil. sedsed 's/ \( *\)/\1/g'
Dewi'nin

"benzer" ;-) not
Jaleks

1

Her ne kadar bir Perl tek astarı ile yapılabilirse (ve yapılsa da), küçük bir C ayrıştırıcısı da çok hızlı olur ve aynı zamanda çok küçüktür (ve umarım çok doğrudur):

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

İle derlendi

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

(program 9kb'den biraz daha azdır)

Örneğin bir boruda kullanın:

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

Bunu denedim ve işe yarıyor gibi görünüyor:

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

sedKomut sadece ilk iki grubu ve döner yakalar.


0

C ++, bunu yaparım:

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

Sınama metin dosyasının içeriğini aynı dizeye değiştirir, ancak harfler arasındaki boşluklar kaldırılır. (Doğru olması için her harf arasında boşluk olmasını gerektirir).


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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.