Ein Skript, das zusätzliche Leerzeichen zwischen Buchstaben im Text löscht


12

Ich habe ein Textdokument, das eine Menge Text enthält, der nach jedem Buchstaben ein zusätzliches Leerzeichen enthält!

Beispiel:

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

Visuell:

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…

Beachten Sie, dass es ein Extra gibt nach jedem Buchstaben Leerzeichen steht, sodass zwischen aufeinanderfolgenden Wörtern zwei Leerzeichen stehen.

Gibt es einen Weg, den ich bekommen kann awkodersed die zusätzlichen Leerzeichen zu löschen? (Leider ist dieses Textdokument sehr umfangreich und die manuelle Bearbeitung würde sehr viel Zeit in Anspruch nehmen.)  Ich schätze, dass dies wahrscheinlich ein viel komplexeres Problem ist, das nur mit einem einfachen Bash-Skript gelöst werden kann, da es auch eine Art Texterkennung geben muss.

Wie kann ich dieses Problem angehen?


2
Es ist trivial, alle Leerzeichen durch nichts zu ersetzen. Aber ich denke, Sie möchten die Wörter trennen?
Sonntag,

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

1
Dies beschränkt die Änderung nicht auf Leerzeichen zwischen Buchstaben . (Ziffern und Satzzeichen sind zum Beispiel keine Buchstaben ). Sie können dies in sed mit einer Schleife tun. Dies ist wahrscheinlich auch ein Duplikat.
Thomas Dickey

1
um nur zwischen Buchstaben einzuschränken: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: Die Quelle der ursprünglichen Überarbeitung zeigt, dass die Leerzeichen zwischen den Wörtern verdoppelt wurden. Warum hast du sie in deiner Bearbeitung entdoppelt?
El'endia Starman

Antworten:


16

Der folgende reguläre Ausdruck entfernt das erste Leerzeichen in einer beliebigen Folge von Leerzeichen. Das sollte den Job machen.

s/ ( *)/\1/g

Also so etwas wie:

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

... ersetzt infile.txt durch eine "feste" Version.


@terdon Ich habe in letzter Zeit bemerkt, dass die Leute aufgehört haben, Perl-Tortenskripte zu schreiben, perl -piewie Ihre Bearbeitung zeigt. Was ist der Grund dafür? Das -pie hat immer gut für mich funktioniert und ist eine großartige Gedächtnisstütze. Hat sich das Verhalten von -i dahingehend geändert, dass alles, was folgt, als Erweiterung behandelt wird und nicht nur die Dinge, die mit einem Punkt beginnen? Es wäre seltsam für sie, etwas so Idiomatisches zu brechen.
Dewi Morgan

1
Na ja, das ist keine Sprache, die ich kenne. Perl ist so, seit ich es benutze -i. Andererseits habe ich es bisher nur auf Linux-Rechnern verwendet und seit einigen Jahren nichts mehr darüber gewusst. Daher kann ich nicht über sein älteres Verhalten sprechen. Auf meinem Rechner aber dies: perl -pie 's/a/b/' f, erzeugt einen Fehler: Can't open perl script "s/o/A/": No such file or directory. While perl -i -pe 's/o/A/' ffunktioniert wie erwartet. Also ja, das ewird als Backup-Erweiterung genommen.
Terdon

Trauriges Gesicht. Ah, nun, die Zeit vergeht, und es bedeutet nur, dass ich eine Parameterreihenfolge neu lernen muss. Hält mein Gehirn quetschend, denke ich. Vielen Dank, dass Sie mich informiert und meinen Code korrigiert haben!
Dewi Morgan

17

Verwenden Sie wordsegmentein NLP-Paket für die reine Python-Wortsegmentierung:

$ 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
Die Verwendung von NLP ist wahrscheinlich die effektivste Lösung, wenn es nichts anderes gibt, das die Wörter voneinander unterscheidet. NLP bietet in den meisten Fällen eine bessere Leistung als ein Look-Ahead-Wörterbuch.
voraussichtlich

13

Basierend auf der Tatsache, dass die Eingabe doppelte Leerzeichen zwischen Wörtern enthält, gibt es eine viel einfachere Lösung. Sie ändern einfach die doppelten Leerzeichen in ein unbenutztes Zeichen, entfernen die Leerzeichen und ändern das unbenutzte Zeichen wieder in ein Leerzeichen:

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'

... Ausgänge:

Das Buch hat auch einen analytischen Zweck, der wichtiger ist


5
Ein sed-Befehl mit der Bedeutung "Ersetze jedes Vorkommen eines Nicht-Leerzeichens, gefolgt von einem Leerzeichen mit nur dem entsprechenden Nicht-Leerzeichen" bewirkt dasselbe:sed -e "s/\([^ ]\) /\1/g"
woodengod

3
Das ist in der Tat eine gute Alternative. Sie sollten es als Antwort posten, um eine Gutschrift dafür zu erhalten.
Julie Pelletier

10

Perl zur Rettung!

Sie benötigen ein Wörterbuch, dh eine Datei mit einem Wort pro Zeile. Auf meinem System existiert es als /var/lib/dict/words, ich habe auch ähnliche Dateien wie /usr/share/dict/britishetc. gesehen.

Zunächst erinnern Sie sich an alle Wörter aus dem Wörterbuch. Anschließend lesen Sie die Eingabe zeilenweise und versuchen, einem Wort Zeichen hinzuzufügen. Wenn es möglich ist, erinnern Sie sich an das Wort und versuchen, den Rest der Zeile zu analysieren. Wenn Sie das Zeilenende erreichen, geben Sie die Zeile aus.

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

Für Ihre Eingabe generiert es 4092 mögliche Messwerte auf meinem System.


a cat a loga c a t a l o g
fehlgeschlagener

@richard: OBOE, behoben. Aber es gibt jetzt zu viele Möglichkeiten, versuchen Sie Wörter mit einem Buchstaben zu entfernen.
Choroba

@richard Sie können dieses Problem mit Hilfe eines nicht deterministischen Algorithmus bekämpfen (z. B. alle möglichen Messwerte werden gespeichert) und einen Parser darauf anwenden. Dann könnten Sie alle 4000 möglichen Messwerte auf die einzige mit der geringsten Fehleranzahl filtern.
Bash0r

6

Hinweis: Diese Antwort (wie auch einige andere hier) basiert auf einer früheren Version der Frage, bei der Wörter nicht getrennt wurden. Die neuere Version kann trivial beantwortet werden .

Bei einer Eingabe wie:

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

Du könntest es versuchen:

 $ 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

Es wird von links nach rechts abgearbeitet und findet ein langes Wort nach dem anderen.

Natürlich ist es hier nicht die beste Wortauswahl, da dieser Satz keinen Sinn ergibt. Um jedoch den richtigen Satz zu finden, benötigen Sie Tools, die die Grammatik oder Bedeutung des Textes oder zumindest einige statistische Informationen verstehen Informationen darüber, welche Wörter wahrscheinlich zusammen gefunden werden, um die wahrscheinlichste Wortgruppe zu finden. Die Lösung scheint eine spezialisierte Bibliothek zu sein, wie sie Lynn gefunden hat


@terdon, siehe bearbeiten. Das Problem ist, dass diese Frage von einer komplexen und interessanten in eine triviale Frage geändert wurde. Gibt es eine Möglichkeit, es in die beiden Fragen aufzuteilen, die es vor und nach der Bearbeitung gab?
Stéphane Chazelas

Ich fürchte nicht, nein. Trotzdem ein cleverer Trick, auch wenn er nicht perfekt ist.
terdon

1
Genau genommen war die Frage von Anfang an trivial - siehe die erste Version und ihre Quelle . Leider hat das OP nicht verstanden, wie Stack Exchange Text rendert, sodass der richtige Eingabetext erst sichtbar wurde, nachdem Trichoplax die Formatierung korrigiert hatte - und noch bedauerlicher war, dass er dann nicht sichtbar war , weil die Person, die die Bearbeitung sofort genehmigte ging und brach es.
Scott

2

Ähnlich wie Dewi Morgans Version, aber mit sed:

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

Das ist sednur GNU und das entspricht nicht Dewi's. Das Standardäquivalent sedzu Dewi's wäresed 's/ \( *\)/\1/g'
Stéphane Chazelas

Beachten Sie die "ähnlich" ;-)
Jaleks

1

Obwohl es mit einem Perl-Einzeiler gemacht werden könnte (und sollte), wäre ein kleiner C-Parser auch sehr schnell und ist auch sehr klein (und hoffentlich sehr korrekt):

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

Kompiliert mit

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

(Programm ist etwas kleiner als 9kb)

Verwenden Sie in einem Rohr wie zB:

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

Ich habe es versucht und es scheint zu funktionieren:

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

Der sedBefehl erfasst zwei Gruppen und gibt nur die erste zurück.


0

In c ++ würde ich dies tun:

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

Ändert den Inhalt der Testtextdatei in dieselbe Zeichenfolge, wobei jedoch Leerzeichen zwischen den Buchstaben entfernt werden. (Um genau zu sein, muss zwischen jedem Buchstaben ein Leerzeichen eingefügt werden.)


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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.