Ищем письмо, которое максимально близко к указанному c слову в регулярном выражении - PullRequest
0 голосов
/ 13 апреля 2020

Я пытаюсь найти некоторые адреса электронной почты в исходном коде и сопоставить их с именем и фамилией человека, с которым они связаны. Первый шаг в моем процессе - найти имя и фамилию кого-то. У меня есть функция, которая делает это очень хорошо и возвращает список с полным именем.

Второй шаг - найти адрес электронной почты, ближайший к этому имени (отображается ли он до имени или после). , Поэтому я ищу и электронное письмо до, и электронное письмо после.

Для этой конкретной цели я написал следующее регулярное выражение:

for name in full_name_list:
        # full name followed by the email
        print(re.findall(name+'.*?([A-z0-9_.-]+?@[A-z0-9_.-]+?\.[A-z]+)', source))
        # email followed by full name
        print(re.findall('([A-z0-9_.-]+?@[A-z0-9_.-]+?\.\w+?.+?'+name+')', source))

Теперь мы заключаем сделку, предполагая, что мой исходный код вот так и вот мой full_name_list=['John Doe', 'James Henry', 'Jane Doe']:

" John Doe is part of our team and here is his email: johndoe@something.com. James Henry is also part of our team and here his email: jameshenry@something.com. Jane Doe is the team manager and you can contact her at that address: janedoe@something.com"

Первый регулярное выражение возвращает имя с ближайшим после него адресом электронной почты, что я и хочу. Однако второе регулярное выражение всегда начинается с первого электронного письма, которое оно находит, и останавливается, когда оно совпадает с именем, что странно, так как я просил найти наименьшее количество символов между письмом и именем .... (или, по крайней мере, я думаю, Я)

Правильно ли мое предположение? Если да, что происходит? Если нет, как я могу это исправить?

Ответы [ 2 ]

1 голос
/ 13 апреля 2020

Проблема заключается в том, что ваш шаблон имеет .*? между шаблонами электронной почты и именами, и поскольку механизм регулярных выражений анализирует строку слева направо, сопоставление начинается с первого сообщения электронной почты, а затем совпадает до самого левого вхождения name, потенциально соответствие любому количеству других электронных писем.

Вы можете использовать

import re
full_name_list=['John Doe', 'James Henry', 'Jane Doe']
source = r" John Doe is part of our team and here is his email: johndoe@something.com. James Henry is also part of our team and here his email: jameshenry@something.com. Jane Doe is the team manager and you can contact her at that address: janedoe@something.com"
for name in full_name_list:
    # full name followed by the email
    name_email = re.search(r'\b' + name+r'\b.*?([\w.-]+@[\w.-]+\.w+)', source)
    if name_email:
        print( 'Email before "{}" keyword: {}'.format(name, name_email.group(1)) )
    # email followed by full name
    email_name = re.search(r'\b([\w.-]+@[\w.-]+\.\w+)(?:(?![\w.-]+@[\w.-]+\.\w).)*?\b'+name+r'\b', source, re.S)
    if email_name:
        print( 'Email after "{}" keyword: {}'.format(name, email_name.group(1)) )

См. Python demo .

Вывод:

Email after "James Henry" keyword: johndoe@something.com
Email after "Jane Doe" keyword: jameshenry@something.com

Примечания :

  • [A-z] соответствует не только буквам ASCII , вы, скорее всего, захотите использовать \w вместо [A-Za-z0-9_] (хотя \w также соответствует любым буквам и цифрам Юникода, но вы можете отключить это поведение, если передаете флаг re.ASCII в re.compile)
  • \b - это граница слова, это Рекомендуется добавить его в начале и конце переменной name, чтобы сопоставлять имена как целые слова
  • (?:(?![\w.-]+@[\w.-]+\.\w).)*? - это fix для вашей текущей проблемы, а именно, этот шаблон соответствует ближайший текст между электронным письмом и последующим именем. Он соответствует любому символу ((?:.)), 0 или более вхождений (*?), который не является начальным символом для шаблона [\w.-]+@[\w.-]+\.\w электронной почты.
0 голосов
/ 13 апреля 2020

Во-первых, отделите электронную почту от домена, чтобы @thing.com находился в другом столбце.

Далее, похоже, вы описываете алгоритм нечеткого сопоставления, называемый расстоянием Левенштейна. Вы можете использовать модуль, разработанный для этого, или, возможно, написать собственный:

import numpy as np
def levenshtein_ratio_and_distance(s, t, ratio_calc = False):
    """ levenshtein_ratio_and_distance:
        Calculates levenshtein distance between two strings.
        If ratio_calc = True, the function computes the
        levenshtein distance ratio of similarity between two strings
        For all i and j, distance[i,j] will contain the Levenshtein
        distance between the first i characters of s and the
        first j characters of t
    """
    # Initialize matrix of zeros
    rows = len(s)+1
    cols = len(t)+1
    distance = np.zeros((rows,cols),dtype = int)

    # Populate matrix of zeros with the indeces of each character of both strings
    for i in range(1, rows):
        for k in range(1,cols):
            distance[i][0] = i
            distance[0][k] = k

    # Iterate over the matrix to compute the cost of deletions,insertions and/or substitutions    
    for col in range(1, cols):
        for row in range(1, rows):
            if s[row-1] == t[col-1]:
                cost = 0 # If the characters are the same in the two strings in a given position [i,j] then the cost is 0
            else:
                # In order to align the results with those of the Python Levenshtein package, if we choose to calculate the ratio
                # the cost of a substitution is 2. If we calculate just distance, then the cost of a substitution is 1.
                if ratio_calc == True:
                    cost = 2
                else:
                    cost = 1
            distance[row][col] = min(distance[row-1][col] + 1,      # Cost of deletions
                                 distance[row][col-1] + 1,          # Cost of insertions
                                 distance[row-1][col-1] + cost)     # Cost of substitutions
    if ratio_calc == True:
        # Computation of the Levenshtein Distance Ratio
        Ratio = ((len(s)+len(t)) - distance[row][col]) / (len(s)+len(t))
        return Ratio
    else:
        # print(distance) # Uncomment if you want to see the matrix showing how the algorithm computes the cost of deletions,
        # insertions and/or substitutions
        # This is the minimum number of edits needed to convert string a to string b
        return "The strings are {} edits away".format(distance[row][col])

Теперь вы можете получить числовое значение того, насколько они похожи. Вам все еще нужно будет установить sh обрезание того, какое число приемлемо для вас.

Str1 = "Apple Inc."
Str2 = "apple Inc"
Distance = levenshtein_ratio_and_distance(Str1.lower(),Str2.lower())
print(Distance)
Ratio = levenshtein_ratio_and_distance(Str1.lower(),Str2.lower(),ratio_calc = True)
print(Ratio)

Существуют и другие алгоритмы подобия, кроме Левенштейна. Вы можете попробовать Jaro-Winkler или Trigram.

Я получил этот код от: https://www.datacamp.com/community/tutorials/fuzzy-string-python

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...