Python中的大容量替换为正则表达式

Python中的大容量替换为正则表达式,python,regex,django,Python,Regex,Django,对于Django应用程序,如果数据库中有与匹配相关的资源,则需要将字符串中所有出现的模式转换为链接 现在,流程如下: -我使用re.sub来处理非常长的文本字符串 -当re.sub找到模式匹配时,它会运行一个函数,查找该模式是否与数据库中的条目匹配 -如果存在匹配项,则会将链接环绕在匹配项周围 问题是,有时数据库上有数百次点击。我希望能够对数据库进行单个批量查询 那么:您可以在Python中使用正则表达式进行批量查找和替换吗 以下代码仅供参考(出于好奇,我查找的模式是法律引用): def添加链接

对于Django应用程序,如果数据库中有与匹配相关的资源,则需要将字符串中所有出现的模式转换为链接

现在,流程如下: -我使用re.sub来处理非常长的文本字符串 -当re.sub找到模式匹配时,它会运行一个函数,查找该模式是否与数据库中的条目匹配 -如果存在匹配项,则会将链接环绕在匹配项周围

问题是,有时数据库上有数百次点击。我希望能够对数据库进行单个批量查询

那么:您可以在Python中使用正则表达式进行批量查找和替换吗

以下代码仅供参考(出于好奇,我查找的模式是法律引用):

def添加链接引用(文本):
链接的_text=re.sub(r'(?P[0-9]+[a-zA-Z]{0,3})\s+(?P[a-Z][a-zA-Z0-9\.\s]{1,49}?\s+(?P[0-9]+[a-zA-Z]{0,3})),创建引用链接,文本)
返回链接的文本
定义创建引用链接(匹配对象):
体积=无
记者=无
页码=无
如果匹配对象组(“卷”)不在[无]、[卷]中:
卷=匹配对象组(“卷”)
如果match_object.group(“reporter”)不在[None]、[None]:
报告者=匹配对象组(“报告者”)
如果匹配对象组(“页面”)不在[无]、[中]:
页面=匹配对象组(“页面”)
如果卷、记者和页面:#这些都应该在这里。。。
# !!! 这就是我一直在访问数据库的地方
引文=引文.对象.过滤器(卷=卷,报告者=报告者,页=页)
如果引文.exists():
引文=引文[0]
文档=引文.document
url=document.url()
返回“”%(url、卷、报告器、页面)
其他:
返回“%s%s%s%”(卷、报告器、页面)

如果这是明显错误的,很抱歉(4小时内没有人提出这一点令人担忧!),但为什么不搜索所有匹配项,对所有内容进行批量查询(一旦找到所有匹配项,很容易),然后使用结果字典调用sub(这样函数就可以从dict中提取数据)


您必须运行两次regexp,但似乎无论如何数据库访问都是昂贵的部分。

您可以使用返回匹配对象的
finditer
通过一次regexp过程来完成

匹配对象具有:

  • 返回命名组dict的方法,
    groupdict()
  • 原始文本中匹配的开始和结束位置,
    span()
  • 原始匹配文本,
    group()
因此,我建议您:

  • 使用
    finditer
  • 列出所有匹配的唯一卷、报告器、页面三元组
  • 查找那些三胞胎
  • 将每个匹配对象与三元组查找的结果(如果找到)关联起来
  • 处理原始文本,按匹配跨距分割并插入查找结果
我通过组合
Q(volume=foo1,reporter=bar2,page=baz3)| Q(volume=foo1,reporter=bar2,page=baz3)列表实现了数据库查找。
。也许有更有效的方法

下面是一个未经测试的实现:

from django.db.models import Q
from collections import namedtuple

Triplet = namedtuple('Triplet',['volume','reporter','page'])

def lookup_references(matches):
  match_to_triplet = {}
  triplet_to_url = {}
  for m in matches:
    group_dict = m.groupdict()
    if any(not(x) for x in group_dict.values()): # Filter out matches we don't want to lookup
      continue
    match_to_triplet[m] = Triplet(**group_dict)
  # Build query
  unique_triplets = set(match_to_triplet.values())
  # List of Q objects
  q_list = [Q(**trip._asdict()) for trip in unique_triplets]
  # Consolidated Q
  single_q = reduce(Q.__or__,q_list)
  for row in Citations.objects.filter(single_q).values('volume','reporter','page','url'):
    url = row.pop('url')
    triplet_to_url[Triplet(**row)] = url
  # Now pair original match objects with URL where found
  lookups = {}
  for match, triplet in match_to_triplet.items():
    if triplet in triplet_to_url:
      lookups[match] = triplet_to_url[triplet]
  return lookups

def interpolate_citation_matches(text,matches,lookups):
  result = []
  prev = m_start = 0
  last = m_end = len(text)
  for m in matches:
    m_start, m_end = m.span()
    if prev != m_start:
      result.append(text[prev:m_start])
    # Now check match
    if m in lookups:
      result.append('<a href="%s">%s</a>' % (lookups[m],m.group()))
    else:
      result.append(m.group())
  if m_end != last:
    result.append(text[m_end:last])
  return ''.join(result)

def process_citations(text):
  citation_regex = r'(?P<volume>[0-9]+[a-zA-Z]{0,3})\s+(?P<reporter>[A-Z][a-zA-Z0-9\.\s]{1,49}?)\s+(?P<page>[0-9]+[a-zA-Z]{0,3}))'
  matches = list(re.finditer(citation_regex,text))
  lookups = lookup_references(matches)
  new_text = interpolate_citation_matches(text,matches,lookups)
  return new_text
从django.db.models导入Q
从集合导入namedtuple
Triplet=namedtuple('Triplet',['volume','reporter','page']))
def查找_引用(匹配项):
匹配_到_三元组={}
三元组\u到\u url={}
对于匹配中的m:
groupdict=m.groupdict()
如果有(不是组中x的(x)_dict.values()):#筛选出我们不想查找的匹配项
持续
匹配三元组[m]=三元组(**组)
#生成查询
unique\u triplets=set(将\u与\u triplet.values()匹配)
#Q对象列表
q_list=[q(**trip.\u asdict())用于唯一的三元组中的trip]
#综合Q
单q=减少(q
对于引文.objects.filter(single_q).values('volume','reporter','page','url')中的行:
url=row.pop('url')
三元组到url[三元组(**行)]=url
#现在将原始匹配对象与找到的URL配对
查找={}
对于match,在match_to_triplet.items()中使用三元组:
如果三元组中的三元组\u到\u url:
查找[匹配]=三元组到url[三元组]
返回查找
def插值引用匹配(文本、匹配、查找):
结果=[]
prev=m\u start=0
last=m_end=len(文本)
对于匹配中的m:
m_开始,m_结束=m.跨度()
如果上一个!=m_开始:
result.append(文本[prev:m_start])
#现在检查匹配
如果m在查找中:
result.append(“”%(查找[m],m.group())
其他:
result.append(m.group())
如果m_结束!=最后:
result.append(文本[m_end:last])
返回“”。加入(结果)
def过程_引用(文本):
引文_regex=r'(?P[0-9]+[a-zA-Z]{0,3})\s+(?P[a-Z][a-zA-Z0-9\.\s]{1,49}?\s+(?P[0-9]+[a-zA-Z]{0,3}))
匹配项=列表(关于finditer(引文_regex,文本))
lookups=查找\u引用(匹配项)
新建文本=插入引用匹配(文本、匹配、查找)
返回新文本

您遗漏了一点。
reporter\u code
来自哪里?非常长的文本字符串有多长?什么是
引文.objects.count()
数千,数百万?很抱歉报告者代码--为了这个问题,我试图将代码复杂化,但忘了删除其中的一部分。至于第二个问题--目前大约为300000,虽然我预计它很快会达到数百万美元,但我原以为会有一个更优雅的解决方案——但也许没有!我会尝试一下,然后让您知道它是如何进行的。我花了一些时间才理解这一点,但我认为我们的想法是在匹配中使用开始/结束对来用python进行替换,对吗?所以在某种意义上,它用python中的手动处理取代了对re的第二次调用?看看哪个是fa会很有趣
from django.db.models import Q
from collections import namedtuple

Triplet = namedtuple('Triplet',['volume','reporter','page'])

def lookup_references(matches):
  match_to_triplet = {}
  triplet_to_url = {}
  for m in matches:
    group_dict = m.groupdict()
    if any(not(x) for x in group_dict.values()): # Filter out matches we don't want to lookup
      continue
    match_to_triplet[m] = Triplet(**group_dict)
  # Build query
  unique_triplets = set(match_to_triplet.values())
  # List of Q objects
  q_list = [Q(**trip._asdict()) for trip in unique_triplets]
  # Consolidated Q
  single_q = reduce(Q.__or__,q_list)
  for row in Citations.objects.filter(single_q).values('volume','reporter','page','url'):
    url = row.pop('url')
    triplet_to_url[Triplet(**row)] = url
  # Now pair original match objects with URL where found
  lookups = {}
  for match, triplet in match_to_triplet.items():
    if triplet in triplet_to_url:
      lookups[match] = triplet_to_url[triplet]
  return lookups

def interpolate_citation_matches(text,matches,lookups):
  result = []
  prev = m_start = 0
  last = m_end = len(text)
  for m in matches:
    m_start, m_end = m.span()
    if prev != m_start:
      result.append(text[prev:m_start])
    # Now check match
    if m in lookups:
      result.append('<a href="%s">%s</a>' % (lookups[m],m.group()))
    else:
      result.append(m.group())
  if m_end != last:
    result.append(text[m_end:last])
  return ''.join(result)

def process_citations(text):
  citation_regex = r'(?P<volume>[0-9]+[a-zA-Z]{0,3})\s+(?P<reporter>[A-Z][a-zA-Z0-9\.\s]{1,49}?)\s+(?P<page>[0-9]+[a-zA-Z]{0,3}))'
  matches = list(re.finditer(citation_regex,text))
  lookups = lookup_references(matches)
  new_text = interpolate_citation_matches(text,matches,lookups)
  return new_text