Сценарій, який видаляє зайві пробіли між літерами в тексті


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…

Зауважте, що після кожної літери є додатковий пробіл, тому між послідовними словами є два пробіли.

Чи є спосіб отримати awkабо sedвидалити зайві пробіли? (На жаль, цей текстовий документ є масовим і потребуватиме дуже довго часу вручну.)  Я розумію, що це, мабуть, набагато складніша проблема, яку потрібно вирішити просто простим скриптом bash, оскільки також має бути якесь розпізнавання тексту.

Як я можу підійти до цієї проблеми?


2
тривіально замінювати всі пробіли нічим .. але я думаю, ви б хотіли розділити слова?
Sundeep

для екс: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: Джерело оригінальної редакції показує, що пробіли між словами були подвоєні. Чому ви скасували їх у подвійному редагуванні?
El'endia Starman

Відповіді:


16

Наступний регулярний вираз видалить перший пробіл у будь-якому рядку пробілів. Це повинно зробити свою роботу.

s/ ( *)/\1/g

Тож щось на кшталт:

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

... замінить infile.txt на "фіксовану" версію.


@terdon Останнім часом я помічав, що люди перестали писати сценарії перламутрового пирога як perl -pie- як показує ваша редакція. Що обґрунтовує це? -Pie завжди добре працював для мене і є чудовим мнеміком. Чи змінилася поведінка -i, щоб розглядати що-небудь наступне як розширення, а не лише те, що починається з крапки? Їм здасться дивним ламати щось таке ідіоматичне.
Деві Морган

1
Так, це не ідіома, з якою я знайомий. Perl був таким способом так довго, як я використовую -i. З іншого боку, я лише коли-небудь використовував його на машинах Linux, і про це я не знав більше декількох років, тому не можу говорити про його стару поведінку. На моїй машині , хоча, це: 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працює як очікувалося. Так що так, the eвважається резервним розширенням.
terdon

Сумне обличчя. Ну, ну, час рухається далі, і це просто означає, що мені потрібно вивчити порядок параметрів. Здається, мій мозок колючий, я думаю. Дякуємо, що повідомили про мене та за виправлення коду!
Деві Морган

17

Використовуйте wordsegmentпакет NLP з сегментацією слів із чистим Python:

$ 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"
woodgod

3
Це дійсно гарна альтернатива. Ви повинні опублікувати його як відповідь, щоб отримати кредит за нього.
Джулі Пелтьє,

10

Перл на допомогу!

Вам потрібен словник, тобто файл із переліком одного слова на рядок. У моїй системі вона існує як /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 loga c a t a l o g
відкладеною

@richard: OBOE, виправлено. Але тепер це створює занадто багато можливостей, спробуйте видалити слова з однієї літери.
choroba

@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

Він обробляє зліва направо і знаходить одне найдовше слово після наступного.

Очевидно, що тут не найкращий підбір слів, оскільки це речення не має сенсу, але щоб придумати правильне, вам знадобляться інструменти, здатні зрозуміти граматику чи значення тексту або хоча б якісь статистичні інформація про те, які слова, ймовірно, знайдуться разом, щоб скласти найбільш ймовірний набір слів. Виглядає, що рішення - це спеціалізована бібліотека, яку знайшла Лінн


@terdon, див. редагування. Проблема полягає в тому, що це питання було змінено зі складного та цікавого на тривіальне. Чи є спосіб ви розділити його на два запитання, які були до та після редагування?
Стефан Шазелас

Боюся, ні, ні. Але все-таки розумний трюк, навіть якщо не ідеальний.
тердон

1
Строго кажучи, питання було тривіальним від початку - дивіться першу версію та її джерело . На жаль, ОП не розуміє , як Stack Обмін робить текст, тому правильне введення тексту не було видно , поки Trichoplax не фіксується форматування - і, ще більш , до жаль, не було видно то , тому що людина , який схвалив це редагувати відразу пішов і зламав його.
Скотт

2

Схожий на версію Деві Моргана, але з sed:

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

Це sedлише GNU, і це не рівнозначно Дьюї. Стандартним sedеквівалентом Деві будеsed 's/ \( *\)/\1/g'
Стефан Шазелас,

зверніть увагу на "подібне" ;-)
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

(програма трохи менше 9 кбіт)

Використовуйте в трубі, наприклад:

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Команда захоплює дві групи і повертає тільки перший.


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.