จะลบส่วนด้านซ้ายของสตริงได้อย่างไร?


144

ฉันมีรหัสหลามง่าย ๆ ที่ค้นหาไฟล์สำหรับสตริงเช่นpath=c:\pathที่c:\pathส่วนอาจแตกต่างกันไป รหัสปัจจุบันคือ:

def find_path(i_file):
    lines = open(i_file).readlines()
    for line in lines:
        if line.startswith("Path="):
            return # what to do here in order to get line content after "Path=" ?

วิธีง่ายๆในการรับข้อความPath=คืออะไร?


โปรดระวังว่าคุณกลับมาที่บรรทัดแรกที่เกิดขึ้นภายในไฟล์ที่ขึ้นต้นด้วย "Path =" คำตอบอื่น ๆ ของโพสต์นี้ก็เช่นกัน แต่ถ้าไฟล์เป็นไฟล์แบทช์ DOS คุณอาจต้องการให้เกิดบรรทัดสุดท้ายจากไฟล์ดังกล่าวโดยขึ้นอยู่กับว่า "แบตช์" หรือไฟล์คำสั่งไม่ได้ถูกเติมด้วยเงื่อนไข
DevPlayer

คำตอบ:


24

เริ่มต้นในPython 3.9คุณสามารถใช้removeprefix:

'Path=helloworld'.removeprefix('Path=')
# 'helloworld'

5
เวลาเดินทางมาก? ;-) จากPEP 596 - Python 3.9 กำหนดการวางตลาด : 3.9.0 รอบชิงชนะเลิศ: วันจันทร์, 2020-10-05
ssc

ฉันกำลังจะเขียนวิธีแก้ปัญหาสำหรับ python 3.9 แต่ดูเหมือนว่าคุณได้พูดถึงโซลูชั่น python 3.9 ทุกที่ :)
Pygirl

196

หากสตริงได้รับการแก้ไขคุณสามารถใช้:

if line.startswith("Path="):
    return line[5:]

ซึ่งให้ทุกอย่างจากตำแหน่งที่ 5 ในสตริง (สตริงก็เป็นลำดับด้วยเช่นกันดังนั้นตัวดำเนินการลำดับเหล่านี้จะทำงานที่นี่เช่นกัน)

หรือคุณสามารถแบ่งบรรทัดในตอนแรก=:

if "=" in line:
    param, value = line.split("=",1)

จากนั้นพารามิเตอร์คือ "เส้นทาง" และค่าคือส่วนที่เหลือหลังจาก = แรก


3
+1 สำหรับวิธีการแยกให้หลีกเลี่ยงความน่าเกลียดเล็กน้อยของการแบ่งด้วยตนเองบน len (คำนำหน้า)
bobince

1
แต่ยังโยนถ้าข้อมูลของคุณไม่ได้อยู่ในรูปแบบ "something = someelse"
Dan Olson

1
นั่นเป็นเหตุผลที่ฉันวางเงื่อนไขไว้ข้างหน้าดังนั้นมันจึงถูกใช้ก็ต่อเมื่อ "=" อยู่ในสตริง มิฉะนั้นคุณสามารถทดสอบความยาวของผลลัพธ์ของการแยก () และหากเป็น == 2
MrTopf

7
เช่นเดียวกับ Dan Olson พูดว่าsplitเกิดข้อยกเว้นหากไม่มีตัวคั่น partitionมีเสถียรภาพมากขึ้นก็ยังแยกสตริงและมักจะส่งกลับ tuple สามองค์ประกอบด้วยก่อน, ตัวคั่นและโพสต์เนื้อหา (ซึ่งบางส่วนอาจจะ''ถ้าตัวคั่นไม่ได้อยู่) เช่นvalue = line.partition('=').
Anders Johansson

1
แยกไม่ส่งข้อยกเว้นถ้าไม่มีตัวคั่นมันส่งกลับรายการด้วยสายอักขระทั้งหมด อย่างน้อยภายใต้ python 2.7
Maxim

122

ลบคำนำหน้าจากสตริง

# ...
if line.startswith(prefix):
   return line[len(prefix):]

แยกในการเกิดขึ้นครั้งแรกของตัวแยกผ่าน str.partition()

def findvar(filename, varname="Path", sep="=") :
    for line in open(filename):
        if line.startswith(varname + sep):
           head, sep_, tail = line.partition(sep) # instead of `str.split()`
           assert head == varname
           assert sep_ == sep
           return tail

แยกไฟล์ INI กับConfigParser

from ConfigParser import SafeConfigParser
config = SafeConfigParser()
config.read(filename) # requires section headers to be present

path = config.get(section, 'path', raw=1) # case-insensitive, no interpolation

ตัวเลือกอื่น


1
เหตุผลหนึ่งที่หายากที่จะเยื้องสามช่องว่างแทนสี่
Bob Stein

25
def remove_prefix(text, prefix):
    return text[len(prefix):] if text.startswith(prefix) else text

1
ฉันชอบอันนี้เพราะคุณสามารถแทนที่ "else text" ด้วย "else False" หรือ "else None" หรือ -type- อื่น ๆ ที่คุณต้องการกลับมาเพื่อระบุว่าบรรทัดในไฟล์ไม่ได้ขึ้นต้นด้วย "Path =" โดยส่วนตัวฉันชอบล้อมรอบผู้ประกอบการที่ประกอบไปด้วยวงเล็บให้โดดเด่น
DevPlayer

19

โดยทั่วไปฉันต้องการสิ่งที่เพื่อนร่วมงานแนะนำเมื่อเร็ว ๆ นี้ ใช้การแทนที่ด้วยสตริงว่าง ง่ายต่อการอ่านรหัสรหัสน้อยลง (บางครั้ง) และความเสี่ยงน้อยลงในการระบุจำนวนอักขระที่ไม่ถูกต้อง ตกลง; ฉันไม่ได้ใช้ Python แต่ในภาษาอื่นฉันชอบวิธีนี้มากกว่า:

rightmost = full_path.replace('Path=', '', 1)

หรือ - เพื่อติดตามความคิดเห็นแรกของโพสต์นี้ - หากควรทำก็ต่อเมื่อบรรทัดเริ่มต้นด้วยPath:

rightmost = re.compile('^Path=').sub('', full_path)

ข้อแตกต่างที่สำคัญกับสิ่งที่ได้รับการแนะนำข้างต้นคือไม่มี "จำนวนเวทมนตร์" (5) ที่เกี่ยวข้องและไม่จำเป็นต้องระบุทั้ง ' 5' และสตริง ' Path=' ในคำอื่น ๆ ที่ฉันชอบวิธีนี้จากการบำรุงรักษารหัส มุมมอง.


ไม่ทำงาน: 'c = Path = a'.replace ("Path =", "", 1) ->' c = a '
jfs

3
ไม่ตรงกับความต้องการดั้งเดิมของสตริงที่ขึ้นต้นด้วย "Path ="
ลูกสุนัข

1
คุณสามารถเปลี่ยนรหัส regex rightmost = re.sub('^Path=', '', fullPath)ที่มีเพียงแค่ วัตถุประสงค์ของcompile()วิธีนี้คือการทำให้สิ่งต่าง ๆ เร็วขึ้นถ้าคุณนำวัตถุที่รวบรวมมาใช้ใหม่ แต่เนื่องจากคุณทิ้งมันไปหลังจากที่คุณใช้มันจะไม่มีผลอะไรเลย โดยทั่วไปแล้วคุณไม่ควรกังวลเกี่ยวกับการเพิ่มประสิทธิภาพนี้
Jim Oldfield

13

ฉันชอบpopทำดัชนี[-1]:

value = line.split("Path=", 1).pop()

ถึง

value = line.split("Path=", 1)[1]
param, value = line.split("Path=", 1)

2
ทางเลือกที่ดีโดยไม่มี "หมายเลขมายากล" มันน่าสังเกตว่างานนี้เพราะstartswithได้รับการทดสอบแล้วsplitจะแบ่ง "ไม่มีอะไร" ก่อนและทุกอย่างอื่นหลังจาก split("Path=", 1)มีความแม่นยำมากขึ้น (ในกรณีที่คำนำหน้าปรากฏขึ้นอีกครั้งในภายหลัง) แต่แนะนำหมายเลขมายากลอีกครั้ง
quornian

1
เวอร์ชันย่อของความคิดเห็นก่อนหน้า (สำคัญมาก): ใช้ได้เฉพาะเมื่อคุณทดสอบด้วย startswith () ก่อน
MarcH


5

เกี่ยวกับ..

>>> line = r'path=c:\path'
>>> line.partition('path=')
('', 'path=', 'c:\\path')

แฝดนี้เป็นหัวคั่นและหาง


วิธีนี้ใช้ไม่ได้ในทุกกรณีด้วยวิธีเดียวกัน หากมีตัวคั่นอยู่ผลลัพธ์จะเป็นรายการที่สาม มิฉะนั้นผลลัพธ์จะเป็นรายการแรก
Ioannis Filippidis

5

วิธีที่ง่ายที่สุดที่ฉันนึกได้คือการแบ่งส่วน:

def find_path(i_file): 
    lines = open(i_file).readlines() 
    for line in lines: 
        if line.startswith("Path=") : 
            return line[5:]

โน้ตสั้น ๆ เกี่ยวกับสัญกรณ์สไลซ์จะใช้ดัชนีสองตัวแทนที่จะเป็นสัญกรณ์ปกติ ดัชนีแรกระบุองค์ประกอบแรกของลำดับที่คุณต้องการรวมไว้ในส่วนแบ่งและดัชนีสุดท้ายคือดัชนีทันทีหลังจากองค์ประกอบสุดท้ายที่คุณต้องการรวมไว้ในส่วนย่อย
เช่น:

sequence_obj[first_index:last_index]

ชิ้นประกอบด้วยองค์ประกอบทั้งหมดระหว่างfirst_indexและlast_indexรวมถึงและไม่ได้first_index last_indexหากไม่ใส่ดัชนีแรกค่าเริ่มต้นจะเริ่มจากลำดับ หากไม่ใส่ดัชนีสุดท้ายจะรวมองค์ประกอบทั้งหมดจนถึงองค์ประกอบสุดท้ายในลำดับ อนุญาตให้ใช้ดัชนีลบได้เช่นกัน ใช้ Google เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับหัวข้อ


4
>>> import re

>>> p = re.compile(r'path=(.*)', re.IGNORECASE)

>>> path = "path=c:\path"

>>> re.match(p, path).group(1)
'c:\\path'

1. ใช้r''สตริงสำหรับเส้นทาง Windows 2. re.match()อาจส่งคืนไม่มี
jfs

3

อีกหนึ่งซับง่ายที่ไม่ได้กล่าวถึงที่นี่:

value = line.split("Path=", 1)[-1]

สิ่งนี้จะทำงานอย่างถูกต้องสำหรับกรณีขอบต่างๆ:

>>> print("prefixfoobar".split("foo", 1)[-1])
"bar"

>>> print("foofoobar".split("foo", 1)[-1])
"foobar"

>>> print("foobar".split("foo", 1)[-1])
"bar"

>>> print("bar".split("foo", 1)[-1])
"bar"

>>> print("".split("foo", 1)[-1])
""



1

หากคุณรู้จักรายการความเข้าใจ:

lines = [line[5:] for line in file.readlines() if line[:5] == "Path="]

มีการแก้ไขที่แนะนำline.startswith(...)คือ 10X เร็วขึ้น การทดสอบของฉันไม่ได้ยืนยันสิ่งนี้ ยินดีที่จะเปลี่ยนแปลงหากมีหลักฐานสนับสนุนที่ให้การยืนยัน
Matthew Schinckel

0

เวอร์ชั่นป๊อปไม่ถูกต้องนัก ฉันคิดว่าคุณต้องการ:

>>> print('foofoobar'.split('foo', 1).pop())
foobar

0

ทำไมไม่ใช้ regex กับ escape? ^จับคู่ส่วนเริ่มต้นของบรรทัดและre.MULTILINEจับคู่กับแต่ละบรรทัด re.escapeมั่นใจได้ว่าการจับคู่ที่แน่นอน

>>> print(re.sub('^' + re.escape('path='), repl='', string='path=c:\path\nd:\path2', flags=re.MULTILINE))
c:\path
d:\path2

0

ลองใช้รหัสต่อไปนี้

if line.startswith("Path="): return line[5:]

1
อะไรคือความแตกต่างระหว่างคำตอบของคุณกับคำตอบที่ยอมรับ? ฉันเห็นว่ามันเป็นในส่วนแรกของคำตอบอื่น ๆ
eyllanesc

-1

ฉันเดาว่านี่คือสิ่งที่คุณกำลังมองหา

    def findPath(i_file) :
        lines = open( i_file ).readlines()
        for line in lines :
            if line.startswith( "Path=" ):
                output_line=line[(line.find("Path=")+len("Path=")):]
                return output_line

-1

โดยไม่ต้องเขียนฟังก์ชั่นนี้จะแยกตามรายการในกรณีนี้ 'นาย | ดร. | นาง' เลือกทุกอย่างหลังจากแยกด้วย [1] จากนั้นแยกอีกครั้งและหยิบองค์ประกอบอะไรก็ได้ ในกรณีด้านล่าง 'Morris' จะถูกส่งคืน

re.split('Mr.|Dr.|Mrs.', 'Mr. Morgan Morris')[1].split()[1]

-1

นี่เป็นเทคนิคที่คล้ายกันมากกับคำตอบอื่น ๆ แต่ไม่มีการดำเนินการกับสตริงซ้ำ ๆ ความสามารถในการบอกว่าคำนำหน้าอยู่ที่นั่นหรือไม่และยังคงอ่านได้ค่อนข้าง:

parts = the_string.split(prefix_to_remove, 1):
    if len(parts) == 2:
        #  do things with parts[1]
        pass
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.