สคริปต์ที่ลบช่องว่างพิเศษระหว่างตัวอักษรในข้อความ


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ลบช่องว่างเพิ่มเติมหรือไม่? (น่าเสียดายที่เอกสารข้อความนี้มีขนาดใหญ่มากและอาจใช้เวลานานมากในการดำเนินการด้วยตนเอง)  ฉันขอขอบคุณที่นี่อาจเป็นปัญหาที่ซับซ้อนมากขึ้นในการแก้ด้วยสคริปต์ทุบตีง่าย ๆ เพราะต้องมีการจดจำข้อความด้วยเช่นกัน

ฉันจะแก้ไขปัญหานี้ได้อย่างไร


2
มันไม่สำคัญที่จะแทนที่ช่องว่างทั้งหมดด้วยอะไร .. แต่ฉันคิดว่าคุณต้องการที่จะแยกคำ?
Sundeep

ตัวอย่างเช่น:echo 't h i s i s a n e x a m p l e' | sed 's/ //g'
Sundeep

1
แต่นั่นไม่ได้ จำกัด การเปลี่ยนแปลงไปยังช่องว่างระหว่างตัวอักษร (เช่นตัวเลขและเครื่องหมายวรรคตอนไม่ใช่ตัวอักษร ) คุณสามารถทำได้ด้วยการวนซ้ำ นี่ก็อาจจะซ้ำกัน
โทมัสผ้ากันเปื้อน

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

regex ต่อไปนี้จะลบช่องว่างแรกในสตริงของช่องว่างใด ๆ ที่ควรทำผลงาน

s/ ( *)/\1/g

ดังนั้นสิ่งที่ชอบ:

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

... จะแทนที่ infile.txt ด้วยรุ่น "แก้ไข"


@terdon ฉันสังเกตเห็นในครั้งล่าสุดที่ผู้คนหยุดเขียนสคริปต์เพอร์เพิลพายเป็นperl -pie- ในขณะที่การแก้ไขของคุณแสดง เหตุผลนี้คืออะไร -pie ทำงานได้ดีสำหรับฉันเสมอและเป็นเครื่องช่วยจำที่ยอดเยี่ยม พฤติกรรมของฉันเปลี่ยนไปเป็นการปฏิบัติอะไรก็ตามที่เป็นส่วนขยายแทนที่จะเป็นแค่สิ่งที่เริ่มต้นด้วยจุดเท่านั้น ดูเหมือนจะเป็นเรื่องแปลกสำหรับพวกเขาที่จะทำลายบางสิ่งที่สำนวนผิด ๆ
Dewi Morgan

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ทำงานตามที่คาดไว้ ดังนั้นใช่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

Perl เพื่อช่วยเหลือ!

คุณต้องการพจนานุกรมคือไฟล์ที่แสดงหนึ่งคำต่อบรรทัด ในระบบของฉันมันมีอยู่/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 logเช่นa c a t a l o g
ctrl-alt-delor

@ Richard: OBOE คงที่ แต่ตอนนี้มันสร้างความเป็นไปได้มากเกินไปพยายามลบคำหนึ่งคำ
choroba

@ Richard คุณอาจต่อสู้กับปัญหานี้ด้วยความช่วยเหลือของอัลกอริทึมที่ไม่ได้กำหนดไว้ (เช่นการอ่านที่เป็นไปได้ทั้งหมดจะถูกเก็บไว้) และใช้ parser กับมัน จากนั้นคุณสามารถกรองการอ่านที่เป็นไปได้ทั้งหมด 4,000 รายการให้เป็นรายการเดียวโดยมีจำนวนข้อผิดพลาดน้อยที่สุด
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
พูดอย่างเคร่งครัดคำถามคือเล็กน้อยจากจุดเริ่มต้น - ดูรุ่นแรกและแหล่งที่มาของ แต่น่าเสียดายที่ OP ไม่เข้าใจวิธีการแลกเปลี่ยนชุดวาทกรรมที่ข้อความเพื่อป้อนข้อความที่ถูกต้องก็จะมองไม่เห็นจนกว่าTrichoplax คงการจัดรูปแบบ - และยิ่งน่าเสียดายที่มันไม่ได้มองเห็นแล้วเพราะคนที่ได้รับการอนุมัติการแก้ไขทันที ไปและทำลายมัน
สกอตต์

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 มาตรฐานsedเทียบเท่าของ Dewi คือ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.