regex ที่จะจับคู่ แต่แทนที่เฉพาะบางส่วนของรูปแบบใน python

Aug 17 2020

ตัวอย่างทั้งหมดที่ฉันพบใน stack overflow นั้นซับซ้อนเกินกว่าที่ฉันจะทำวิศวกรรมย้อนกลับได้

ลองพิจารณาตัวอย่างของเล่นนี้

s = "asdfasd a_b dsfd"

ฉันต้องการ s = "asdfasd a'b dsfd"

นั่นคือ: ค้นหาอักขระสองตัวที่คั่นด้วยเครื่องหมายขีดล่างและแทนที่ขีดล่างด้วยเครื่องหมายอะพอสทรอฟี

พยายาม:

re.sub("[a-z](_)[a-z]","'",s)
# "asdfasd ' dsfd"

ฉันคิดว่า()ควรจะแก้ปัญหานี้?

สิ่งที่น่าสับสนยิ่งกว่านั้นก็คือความจริงที่ว่าเราพบตัวละครที่เราต้องการแทนที่สำเร็จแล้ว:

re.findall("[a-z](_)[a-z]",s)
#['_']

ทำไมสิ่งนี้ไม่ถูกแทนที่

ขอบคุณ

คำตอบ

3 Marat Aug 17 2020 at 00:27

ใช้รูปแบบมองไปข้างหน้าและมองข้างหลัง:

re.sub("(?<=[a-z])_(?=[a-z])","'",s)

รูปแบบมองไปข้างหน้า / ข้างหลังมีความกว้างเป็นศูนย์ดังนั้นจึงไม่แทนที่สิ่งใด

UPD:

  • ปัญหาคือre.subจะแทนที่นิพจน์ที่ตรงกันทั้งหมดรวมถึงตัวอักษรก่อนหน้าและตัวอักษรต่อไปนี้
  • re.findallยังคงจับคู่นิพจน์ทั้งหมด แต่ยังมีกลุ่ม (วงเล็บด้านใน) ซึ่งคุณสังเกตเห็น ทั้งแมตช์นิ่งa_b
  • นิพจน์ lookahead / lookbehind ตรวจสอบว่าการค้นหาอยู่ข้างหน้า / ตามด้วยรูปแบบ แต่อย่ารวมไว้ในการจับคู่
  • อีกทางเลือกหนึ่งคือการสร้างหลายกลุ่มและนำกลุ่มเหล่านั้นมาแทนที่: re.sub("([a-z])_([a-z])", r"\1'\2", s)
2 RyszardCzech Aug 17 2020 at 02:48

เมื่อใช้re.subต้องจับข้อความที่จะเก็บข้อความที่จะลบไม่ควร

ใช้

re.sub(r"([a-z])_(?=[a-z])",r"\1'",s)

ดูหลักฐาน

คำอธิบาย

NODE                     EXPLANATION
--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  _                        '_'
--------------------------------------------------------------------------------
  (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of look-ahead

รหัส Python :

import re
s = "asdfasd a_b dsfd"
print(re.sub(r"([a-z])_(?=[a-z])",r"\1'",s))

เอาท์พุต:

asdfasd a'b dsfd
ShinNShirley Sep 30 2020 at 14:33

re.subจะเข้ามาแทนที่ทุกอย่างที่มันจับคู่

มีวิธีที่กว้างกว่าในการแก้ปัญหาของคุณและคุณไม่จำเป็นต้องแก้ไขนิพจน์ทั่วไปของคุณอีกครั้ง

รหัสด้านล่าง:

import re


s = 'Data: year=2018, monthday=1, month=5, some other text'
reg = r"year=(\d{4}), monthday=(\d{1}), month=(\d{1})"


r = "am_replace_str"
def repl(match):
    _reg = "|".join(match.groups())
    return re.sub(_reg, r,match.group(0)) if _reg else r

# 
re.sub(reg,repl, s)

เอาต์พุต: 'Data: year=am_replace_str, monthday=am_replace_str, month=am_replace_str, some other text'

แน่นอนว่าหากกรณีของคุณไม่มีกลุ่มรหัสของคุณอาจเป็นเช่นนี้:

import re


s = 'Data: year=2018, monthday=1, month=5, some other text'
reg = r"year=(\d{4}), monthday=(\d{1}), month=(\d{1})"


r = "am_replace_str"
def repl(match):
    _reg = "|".join(match.groups())
    return re.sub(_reg, r,match.group(0))

# 
re.sub(reg,repl, s)