Một tập lệnh xóa khoảng trắng thừa giữa các chữ cái trong văn bản


12

Tôi có một tài liệu văn bản có tải văn bản có thêm khoảng trắng được thêm vào sau mỗi chữ cái!

Thí dụ:

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ực quan:

Th␣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␣ mp␣o␣r␣t␣a␣n␣t

Lưu ý rằng có một khoảng trắng thừa sau mỗi chữ cái, vì vậy có hai khoảng trắng giữa các từ liên tiếp.

Có cách nào để tôi có thể nhận awkhoặc sedxóa các khoảng trắng thừa không? (Thật không may, tài liệu văn bản này rất lớn và sẽ mất nhiều thời gian để hoàn thành thủ công.)  Tôi đánh giá cao rằng đây có lẽ là một vấn đề phức tạp hơn nhiều để giải quyết chỉ với một tập lệnh bash đơn giản vì cũng cần phải có một số nhận dạng văn bản.

Làm thế nào tôi có thể tiếp cận vấn đề này?


2
Thật là tầm thường khi thay thế tất cả các không gian bằng không có gì .. nhưng tôi nghĩ bạn muốn tách các từ?
Sundeep

ví dụ:echo 't h i s i s a n e x a m p l e' | sed 's/ //g'
Sundeep

1
Điều đó không giới hạn sự thay đổi khoảng trắng giữa các chữ cái . ( Chẳng hạn, chữ số và dấu chấm câu không phải là chữ cái ). Bạn có thể làm điều này trong sed với một vòng lặp. Đây cũng có thể là một bản sao.
Thomas Dickey

1
chỉ giới hạn giữa các chữ cái: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: Nguồn gốc của bản sửa đổi ban đầu cho thấy khoảng cách giữa các từ được nhân đôi. Tại sao bạn lại nhân đôi chúng trong chỉnh sửa của mình?
El'endia Starman

Câu trả lời:


16

Regex sau đây sẽ loại bỏ khoảng trắng đầu tiên trong bất kỳ chuỗi khoảng trắng nào. Điều đó nên làm công việc.

s/ ( *)/\1/g

Vì vậy, một cái gì đó như:

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

... sẽ thay thế infile.txt bằng phiên bản "cố định".


@terdon Tôi đã nhận thấy trong thời gian gần đây rằng mọi người đã ngừng viết các tập lệnh perl pie như perl -pie- như chỉnh sửa của bạn cho thấy. Lý do cho việc này là gì? The -pie đã luôn làm việc tốt cho tôi, và là một người ghi nhớ tuyệt vời. Hành vi của - đã thay đổi để coi bất cứ điều gì sau đây là một phần mở rộng, thay vì chỉ những điều bắt đầu bằng dấu chấm? Nó có vẻ kỳ lạ đối với họ để phá vỡ một cái gì đó rất thành ngữ.
Dewi Morgan

1
Huh, nó không phải là một thành ngữ tôi quen thuộc. Perl đã theo cách này miễn là tôi đã sử dụng -i. Mặt khác, tôi chỉ từng sử dụng nó trên các máy Linux và tôi đã không biết về nó trong hơn một vài năm, vì vậy tôi không thể nói về hành vi cũ của nó. Trên máy của tôi, điều này : perl -pie 's/a/b/' f, tạo ra một lỗi : Can't open perl script "s/o/A/": No such file or directory. Trong khi perl -i -pe 's/o/A/' fhoạt động như mong đợi. Vì vậy, có, eđược thực hiện như là phần mở rộng sao lưu.
terdon

Gương mặt buồn. Ah, tốt, thời gian trôi qua, và nó chỉ có nghĩa là tôi cần học lại một thứ tự tham số. Giữ cho bộ não của tôi yếu đuối, tôi đoán. Cảm ơn đã cho tôi biết, và đã sửa mã của tôi!
Dewi Morgan

17

Sử dụng wordsegment, gói NLP phân đoạn từ thuần 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
Sử dụng NLP có lẽ là giải pháp hiệu quả nhất nếu không có gì khác để phân biệt các từ. NLP thực hiện tốt hơn một từ điển nhìn về phía trước trong hầu hết các trường hợp.
hóa dầu

13

Dựa trên thực tế là đầu vào bao gồm khoảng trắng kép giữa các từ, có một giải pháp đơn giản hơn nhiều. Bạn chỉ cần thay đổi khoảng trắng kép thành ký tự không sử dụng, xóa khoảng trắng và thay đổi ký tự không sử dụng trở lại khoảng trắng:

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'

... đầu ra:

Cuốn sách cũng có một mục đích phân tích quan trọng hơn


5
Một lệnh sed với ý nghĩa "thay thế mọi sự xuất hiện của một nhân vật không phải không gian, theo sau là một khoảng sed -e "s/\([^ ]\) /\1/g"
trắng

3
Đó thực sự là một lựa chọn tốt. Bạn nên đăng nó như một câu trả lời để có được tín dụng cho nó.
Julie Pelletier

10

Perl để giải cứu!

Bạn cần một từ điển, tức là một tệp liệt kê một từ trên mỗi dòng. Trên hệ thống của tôi, nó tồn tại như /var/lib/dict/words, tôi cũng đã thấy các tệp tương tự như /usr/share/dict/britishv.v.

Đầu tiên, bạn nhớ tất cả các từ trong từ điển. Sau đó, bạn đọc từng dòng đầu vào và cố gắng thêm các ký tự vào một từ. Nếu có thể, bạn nhớ từ đó và cố gắng phân tích phần còn lại của dòng. Nếu bạn đến cuối dòng, bạn xuất ra dòng.

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

Đối với đầu vào của bạn, nó tạo ra 4092 bài đọc có thể có trên hệ thống của tôi.


thử nghiệm thất bại với phiên bản cách biệt của a cat a logtức làa c a t a l o g
ctrl-alt-delor

@richard: OBOE, đã sửa. Nhưng bây giờ nó tạo ra quá nhiều possibilites, hãy cố gắng loại bỏ một chữ cái.
choroba

@richard Bạn có thể chống lại vấn đề này với sự trợ giúp của thuật toán không xác định (ví dụ: tất cả các bài đọc có thể được lưu trữ) và áp dụng trình phân tích cú pháp trên nó. Sau đó, bạn có thể lọc tất cả 4000 bài đọc có thể thành một lần đọc với số lỗi ít nhất.
bash0r

6

Lưu ý: câu trả lời này (giống như một vài câu hỏi khác ở đây) dựa trên phiên bản trước của câu hỏi mà các từ không được phân định. Phiên bản mới hơn có thể được trả lời một cách tầm thường .

Trên một đầu vào như:

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

Bạn có thể thử:

 $ 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

Nó xử lý từ trái sang phải và tìm một từ dài nhất sau từ tiếp theo.

Rõ ràng, ở đây, đây không phải là lựa chọn từ tốt nhất vì câu đó không có ý nghĩa gì, nhưng để đưa ra từ đúng, bạn cần các công cụ có thể hiểu ngữ pháp hoặc nghĩa của văn bản hoặc ít nhất là một số thống kê thông tin về những từ có khả năng được tìm thấy cùng nhau để đưa ra tập hợp các từ có thể xảy ra nhất. Có vẻ như giải pháp là một thư viện chuyên ngành được tìm thấy bởi Lynn


@terdon, xem chỉnh sửa. Vấn đề là câu hỏi đó đã được thay đổi từ một câu hỏi phức tạp và thú vị thành một câu hỏi nhỏ. Có cách nào bạn có thể chia nó thành hai câu hỏi trước và sau khi chỉnh sửa không?
Stéphane Chazelas

Tôi không sợ, không. Vẫn là một mẹo thông minh, ngay cả khi không hoàn hảo.
terdon

1
Nói đúng ra, câu hỏi rất tầm thường ngay từ đầu - hãy xem phiên bản đầu tiênnguồn của nó . Thật không may, OP không hiểu làm thế nào stack Trao đổi làm cho văn bản, do đó nhập văn bản đúng là không nhìn thấy được cho đến khi Trichoplax adhaerens cố định các định dạng - và, thậm chí nhiều không may, nó không thể nhìn thấy sau đó , vì người đã được phê duyệt mà chỉnh sửa ngay lập tức đã đi và phá vỡ nó.
Scott

2

Tương tự như phiên bản của Dewi Morgan, nhưng với sed:

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

Đó chỉ là GNU sedvà không tương đương với Dewi. sedTương đương tiêu chuẩn của Dewi's sẽ làsed 's/ \( *\)/\1/g'
Stéphane Chazelas

lưu ý "tương tự" ;-)
Jaleks

1

Mặc dù nó có thể (và nên) được thực hiện với một lớp lót Perl, một trình phân tích cú pháp C nhỏ cũng sẽ rất nhanh, và cũng rất nhỏ (và hy vọng rất chính xá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);
}

Tổng hợp với

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

(chương trình nhỏ hơn 9kb một chút)

Sử dụng trong một đường ống như vd:

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

Tôi đã thử điều này và nó dường như hoạt động:

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

Các sedlệnh bắt hai nhóm và trả về chỉ là người đầu tiên.


0

Trong c ++, tôi sẽ làm điều này:

#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ẽ thay đổi nội dung của tệp văn bản kiểm tra, thành cùng một chuỗi, nhưng có khoảng trắng giữa các chữ cái bị xóa. (Nó đòi hỏi một khoảng trống giữa mỗi chữ cái để chính xác).


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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.