텍스트 문자 사이의 여분의 공백을 삭제하는 스크립트


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 에서이 작업을 수행 할 수 있습니다. 이것은 아마도 복제본 일 것입니다.
Thomas Dickey

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편집 쇼처럼 펄 파이 스크립트 작성을 중단했다는 것을 알았습니다 . 이것의 근거는 무엇입니까? 파이는 항상 나를 위해 잘 작동했으며 위대한 니모닉입니다. -i의 동작이 점으로 시작하는 것만이 아니라 확장으로 다음을 처리하도록 변경 되었습니까? 그들이 관용적 인 것을 깨뜨리는 것이 이상하게 보일 것입니다.
Dewi Morgan

1
허, 잘 알고있는 관용구는 아닙니다. Perl은을 사용하는 한 오랫동안 사용되었습니다 -i. 반면에, 나는 리눅스 머신에서만 그것을 사용했으며 몇 년 이상 그것에 대해 알지 못했기 때문에 오래된 행동에 대해서는 말할 수 없습니다. 그래도 내 컴퓨터에서 다음과 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작품이 예상대로. 예,는 e백업 확장명으로 간주됩니다.
terdon

슬픈 얼굴. 아, 시간이 갈수록 매개 변수 순서를 다시 학습해야한다는 의미입니다. 내 뇌를 졸졸 유지합니다. 알려주고 코드를 수정 해 주셔서 감사합니다!
Dewi Morgan

17

wordsegment순수 파이썬 단어 세분화 NLP 패키지 인를 사용하십시오 .

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

3
그것은 실제로 좋은 대안입니다. 크레딧을 얻으려면 답변으로 게시해야합니다.
Julie Pelletier

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
CTRL-ALT-delor

@ 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

왼쪽에서 오른쪽으로 처리하고 다음 단어 중에서 가장 긴 단어를 찾습니다.

분명히, 여기, 문장이 의미가 없기 때문에 가장 좋은 단어 선택은 아니지만 올바른 단어를 찾으려면 텍스트의 문법이나 의미를 이해하거나 최소한 통계를 이해할 수있는 도구가 필요합니다 어떤 단어가 함께 발견되어 가장 가능성이 높은 단어 집합을 찾을 수 있는지에 대한 정보. Lynn이 찾은 솔루션이 특수 라이브러리 인 것처럼 보입니다.


@terdon, 편집 참조. 문제는 그 질문이 복잡하고 흥미로운 질문에서 사소한 질문으로 바뀌 었다는 것입니다. 편집 전후의 두 가지 질문으로 나눌 수있는 방법이 있습니까?
Stéphane Chazelas

아니, 두렵지 않아 비록 완벽하지는 않더라도 여전히 영리한 속임수입니다.
terdon

1
엄밀히 말해, 질문은 처음부터 사소한 것이 었습니다 – 첫 번째 버전그 출처를보십시오 . 스택 Exchange가 텍스트를 렌더링하는 방법 불행하게도, 영업 이익은 때까지 정확한 입력 텍스트가 보이지 않았다, 그래서 이해하지 못했다 trichoplax이 서식 고정 - 더 불행하게도, 그것은 보이지 않았다 다음 , 때문에 즉시 그 편집을 승인 한 사람 가서 파산했다.
Scott

2

Dewi Morgan의 버전과 유사하지만 sed가 있습니다.

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

그것은 GNU sed에만 해당되며 Dewi와 동일하지 않습니다. sedDewi와 동등한 표준은 다음 과 같습니다.sed 's/ \( *\)/\1/g'
Stéphane Chazelas

"유사한";-) 참고
Jaleks

1

비록 그것이 Perl one-liner로 수행 될 수는 있지만, 작은 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

나는 이것을 시도하고 작동하는 것 같다 :

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.