用于名称标准化的

时间:2018-05-31 06:06:44

标签: python regex

我正在尝试编写一个正则表达式来标准化名称。

用例:

J. J. Abrams -> JJ Abrams
J J Abrams -> JJ Abrams
J.J Abrams -> JJ Abrams
J.J. Abrams -> JJ Abrams
J J  Abrams -> JJ Abrams (multiple spaces)

首字母可以出现在名称的末尾或中间。通常,首字母可以有空格或'。'或者在它之前或之后的单词边界。

所以我想出了以下内容:

p = re.compile(r'((\b|\s+|\.)[a-z](\.|\s+|\b))', re.I)

当我尝试匹配并打印结果时,它看起来不对:

p.subn(lambda g: g.groups()[0].strip().strip('.'), "J J Abrams")
('JJAbrams', 2)

如何在非初始部分之前(或之后)保留空间?

修改 此外,我应该说清楚,名称中不能只有2个首字母。以上只是一个随机用例。感谢

5 个答案:

答案 0 :(得分:3)

对于给出的案例,替换

(?<=\b[A-Z]\b)[. ]+(?=[A-Z]\b)|\.|(\s)\s+

$1

应该这样做。

使用交替,在首字母,点之间或多于一个空格之间匹配空格和点。后者抓住了第一个空间。

$1替换它会从前两个替换中删除匹配,在第三种情况下(几个空格)用一个替换它们(第一个被捕获)。

See it here at regex101

答案 1 :(得分:1)

我认为你可以通过使用正则表达式分两步完成:

第1步:

正则表达式:

 +|\. *

和替换(单个空格)

step 1 demo

第2步:

正则表达式:

\b([a-z]) ([a-z])\b

替换:\1\2

step 2 demo

通过组合你拥有的一切:

输入文件:

$ cat names
J. J. Abrams
J J Abrams
J.J Abrams
J.J. Abrams
J J  Abrams
J  Abrams J.
Abrams J. J.
Abrams J J

python代码:

$ cat names_norm.py 
import re
import sys

with open("names") as file:
        for line in file:
                line = re.sub(r" +|\. *", " ", line)
                line = re.sub(r"\b([a-zA-Z]) ([a-zA-Z])\b", "\g<1>\g<2>", line)
                sys.stdout.write(line)
sys.stdout.flush()

<强>输出:

$ python names_norm.py                                                                                                           
JJ Abrams
JJ Abrams
JJ Abrams
JJ Abrams
JJ Abrams
J Abrams J 
Abrams JJ 
Abrams JJ

答案 2 :(得分:0)

使用:

re.sub(r'(?<!\w)([A-Z])\.*\s*(?<!\w)([A-Z])\.*\s*([A-Za-z]*)', r'\1\2 \3', s)

<强>代码

>>> s = 'J. J. Abrams'
>>> re.sub(r'(?<!\w)([A-Z])\.*\s*(?<!\w)([A-Z])\.*\s*([A-Za-z]*)', r'\1\2 \3', s)
JJ Abrams

>>> s = 'J J Abrams'
>>> re.sub(r'(?<!\w)([A-Z])\.*\s*(?<!\w)([A-Z])\.*\s*([A-Za-z]*)', r'\1\2 \3', s)
JJ Abrams

>>> s = 'J.J Abrams'
>>> re.sub(r'(?<!\w)([A-Z])\.*\s*(?<!\w)([A-Z])\.*\s*([A-Za-z]*)', r'\1\2 \3', s)
JJ Abrams

>>> s = 'J.J.  Abrams'
>>> re.sub(r'(?<!\w)([A-Z])\.*\s*(?<!\w)([A-Z])\.*\s*([A-Za-z]*)', r'\1\2 \3', s)
JJ Abrams

>>> s = 'J J      Abrams'
>>> re.sub(r'(?<!\w)([A-Z])\.*\s*(?<!\w)([A-Z])\.*\s*([A-Za-z]*)', r'\1\2 \3', s)
JJ Abrams

答案 3 :(得分:0)

您可以尝试查找所有连续字母并使用以下格式打印:

is_array

结果:

import re
if __name__=='__main__': 
    names = ["J. J. Abrams", "J J Abrams", "J.J Abrams", "J.J. Abrams", "J J  Abrams", "J J J  Abrams"]
    for name in names:
        res = re.findall("([a-z]+)", name, re.I)       #Find all continuous alphabets
        res.insert(len(res)-1, " ").                   #Insert <space> at second last position 
        print("res : %s" % ("".join(map(str, res))))   #Join and display list which is formatted

答案 4 :(得分:0)

因为您只想过滤掉“。”我建议只使用标准的字符串方法。

  1. 将所有点字符替换为一个空格。
  2. 遍历由空格分隔的所有子串 - 每个都被剥离 来自前导和尾随空格
  3. 将一个前导空格和一个尾随空格添加到比一个字符长的元素。
  4. 再次将部件放回原位。
  5. 从前导和尾随空格字符中删除整个字符串。
  6. 为了您的愉快,我把它写成一个完全不可读的单行。

    names = ['J. R. R. Tolkien',  # "." and " "
             'Abrams  J J',       # " "
             'J.J Abrams',        # "." inbetween
             'J.R.R. Tolkien',    # "."
             'J R.R Tolkien']     # mixed
    
    for name in names :
        name = "".join([(" {} ".format(elem)) if len(elem)>1 else elem for elem in name.replace('.', ' ').split()]).strip()
    
        print name
    

    这导致了这个输出。

    JRR Tolkien
    Abrams JJ
    JJ Abrams
    JRR Tolkien
    JRR Tolkien
    

    修改

    @ClasG的解决方案也可能不可读。但我的解决方案甚至需要两倍的时间进行计算。

    Elapsed time, mean value: 6.92432632016e-06
    Elapsed time, mean value: 1.5598555044e-05