删除文本中字母之间多余空格的脚本


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…

请注意,每个字母后都有一个多余的空格,因此连续单词之间有两个空格。

有什么方法可以获取awksed删除多余的空格?(不幸的是,此文本文档非常庞大,需要很长时间才能手动完成。)  我很欣赏这可能是一个简单的bash脚本要解决的复杂得多的问题,因为还需要某种形式的文本识别。

我该如何解决这个问题?


2
将所有空格都替换为空是很简单的。.但是我想您想分隔单词吗?
Sundeep's

例如: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

Answers:


16

以下正则表达式将删除任何空格字符串中的第一个空格。那应该做的。

s/ ( *)/\1/g

所以像这样:

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

...将用“固定”版本替换infile.txt。


@terdon我最近注意到,人们已经停止将perl pie脚本编写为perl -pie-如您的编辑所示。这有什么理由?-pie一直对我有效,并且是一个很好的助记符。-i的行为是否已更改为将以下内容视为扩展,而不是仅将那些以点开头的内容视为扩展?对于他们来说,打破如此惯用的做法似乎很奇怪。
Dewi Morgan

1
嗯,这不是我所熟悉的习语。一直以来,Perl一直采用这种方式-i。另一方面,我只在Linux机器上使用过它,而且几年来我还不知道它,所以我不能说它的较早行为。不过,在我的机器上,this: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,一个纯Python的词段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"
woodengod16年

3
这确实是一个很好的选择。您应该将其发布为答案以赢得信誉。
Julie Pelletier'9

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 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,请参阅编辑。问题是该问题从一个复杂而有趣的问题变成了一个琐碎的问题。有没有办法将其分为编辑之前和之后的两个问题?
斯特凡Chazelas

恐怕不是。即使不是很完美,它仍然是一个聪明的把戏。
terdon

1
严格地说,这个问题从一开始就微不足道的-看到的第一个版本它的来源。不幸的是,OP不明白堆栈Exchange如何呈现文本,所以正确的输入文本是不可见的,直到丝盘虫 固定的格式 -并且,更不幸的是,它是不可见的,然后,因为谁立即批准了编辑的人去打破了它。
斯科特(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。该标准sed黛维的相当于是将sed 's/ \( *\)/\1/g'
斯特凡Chazelas

请注意“相似” ;-)
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

(程序少于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


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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.