Jak usunąć lewą część sznurka?

144

Mam prosty kod w Pythonie, który wyszukuje w plikach ciąg np. path=c:\pathGdzie c:\pathczęść może się różnić. Obecny kod to:

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=" ?

Jaki jest prosty sposób na uzyskanie tekstu Path=?

grigoryvp
źródło
Pamiętaj, że powracasz do wystąpienia pierwszego wiersza w pliku, który zaczyna się od „Path =”. Inne odpowiedzi na ten post też się sprawdzają. Ale jeśli plik jest czymś w rodzaju pliku wsadowego DOS, możesz faktycznie chcieć ostatniego wystąpienia wiersza z takiego pliku, w zależności od tego, czy plik „wsadowy” lub plik poleceń nie jest wypełniony warunkami.
DevPlayer

Odpowiedzi:

23

Począwszy od Python 3.9możesz użyć removeprefix:

'Path=helloworld'.removeprefix('Path=')
# 'helloworld'
Xavier Guihot
źródło
5
dużo podróżuje w czasie? ;-) z PEP 596 - Python 3.9 Harmonogram wydania : 3.9.0 wersja ostateczna: poniedziałek, 05.10.2020
ssc
Miałem zamiar napisać rozwiązanie dla Pythona 3.9, ale wygląda na to, że wspominałeś o rozwiązaniach dla Pythona 3.9 wszędzie. :)
Pygirl
196

Jeśli ciąg jest naprawiony, możesz po prostu użyć:

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

co daje ci wszystko od pozycji 5 w ciągu (łańcuch jest również sekwencją, więc te operatory sekwencji również działają tutaj).

Lub możesz podzielić linię na początku =:

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

Wtedy parametr to „Path”, a wartość to reszta po pierwszym =.

MrTopf
źródło
3
+1 dla metody dzielenia, unika lekkiej brzydoty ręcznego krojenia na len (przedrostek).
bobince
1
Ale wyrzuca również, jeśli wszystkie dane wejściowe nie mają postaci „coś = coś innego”.
Dan Olson
1
Dlatego umieszczam warunek na początku, aby był używany tylko wtedy, gdy w ciągu znajduje się „=”. W przeciwnym razie możesz również sprawdzić długość wyniku split () i czy jest to == 2.
MrTopf
7
Jak mówi Dan Olson, splitzgłasza wyjątek, jeśli nie ma separatora. partitionjest bardziej stabilny, dzieli również ciąg i zawsze zwraca trzyelementową krotkę z pre-, separatorem i post-treścią (niektóre z nich mogą być, ''jeśli separator nie był obecny). Np value = line.partition('=').
Anders Johansson,
1
Split nie zgłasza wyjątku, jeśli nie ma elementu rozdzielanego, zwraca listę z całym ciągiem. Przynajmniej pod Pythonem 2.7
Maxim
122

Usuń prefiks z ciągu

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

Podziel przy pierwszym wystąpieniu separatora przez 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

Przeanalizuj plik podobny do INI za pomocą 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

Inne opcje

jfs
źródło
1
Jeden rzadki powód, aby wciąć trzy spacje zamiast czterech.
Bob Stein
25
def remove_prefix(text, prefix):
    return text[len(prefix):] if text.startswith(prefix) else text
David Foster
źródło
1
Podoba mi się ten, ponieważ możesz zamienić „else text” na „else False” lub „else None” lub cokolwiek -type- chcesz zwrócić, aby wskazać, że wiersz w pliku nie zaczynał się od „Path =”. Osobiście lubię otaczać moje operatory trójskładnikowe nawiasami, aby wyróżnić się wizualnie.
DevPlayer
19

Ogólnie rzecz biorąc, do cięcia (warunkowego lub bezwarunkowego) wolę to, co ostatnio zaproponował kolega; Użyj zamiany z pustym ciągiem. Łatwiejszy do odczytania kod, mniej kodu (czasami) i mniejsze ryzyko podania niewłaściwej liczby znaków. Dobrze; Nie używam Pythona, ale w innych językach wolę takie podejście:

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

lub - aby przejść do pierwszego komentarza do tego posta - jeśli należy to zrobić tylko wtedy, gdy wiersz zaczyna się od Path:

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

Główną różnicą w stosunku do niektórych z tego, co zostało zasugerowane powyżej, jest to, że nie ma tu „magicznej liczby” (5) ani nie ma potrzeby określania zarówno „ 5”, jak i ciągu „ Path=”. Innymi słowy, wolę to podejście od konserwacji kodu punkt widzenia.

fredarin
źródło
To nie działa: 'c = Path = a'.replace ("Path =", "", 1) -> "c = a".
jfs
3
To nie spełnia pierwotnego wymagania ciągu zaczynającego się od „Path =”.
Szczeniak
1
Możesz zamienić kod regex na just rightmost = re.sub('^Path=', '', fullPath). Celem tej compile()metody jest przyspieszenie działania, jeśli ponownie użyjesz skompilowanego obiektu, ale ponieważ wyrzucasz go po użyciu, i tak nie ma to żadnego efektu. Zwykle i tak nie warto martwić się o tę optymalizację.
Jim Oldfield
13

Wolę popindeksować [-1]:

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

do

value = line.split("Path=", 1)[1]
param, value = line.split("Path=", 1)
Thomas Schreiber
źródło
2
Fajna alternatywa bez „magicznych liczb”. Warto zauważyć, że to działa, ponieważ startswithzostało już przetestowane, więc splitpodzieli „nic” przed i wszystko inne po. split("Path=", 1)jest bardziej precyzyjny (w przypadku ponownego pojawienia się przedrostka w dalszej części ciągu), ale ponownie wprowadza magiczną liczbę.
quornian
1
Krótsza wersja (bardzo ważnego) poprzedniego komentarza: działa to TYLKO wtedy, gdy najpierw przetestujesz za pomocą startedwith ().
MarcH
12

Albo dlaczego nie

if line.startswith(prefix):
    return line.replace(prefix, '', 1)
John Damen
źródło
5

Co powiesz na..

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

Ta trójka to głowa, separator i ogon .

Floggedhorse
źródło
To nie działa we wszystkich przypadkach w ten sam sposób. Jeśli separator jest obecny, wynikiem jest trzecia pozycja. W przeciwnym razie wynikiem jest pierwsza pozycja.
Ioannis Filippidis
5

Najprościej przychodzi mi do głowy krojenie:

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

Krótka uwaga na temat notacji wycinków, używa dwóch indeksów zamiast zwykłego. Pierwszy indeks wskazuje pierwszy element sekwencji, który chcesz umieścić w wycinku, a ostatni indeks to indeks występujący bezpośrednio po ostatnim elemencie, który chcesz umieścić w wycinku.
Na przykład:

sequence_obj[first_index:last_index]

Kawałek składa się ze wszystkich elementów między first_indexa last_index, w tym first_indexi nie last_index. Jeśli pierwszy indeks zostanie pominięty, domyślnie jest to początek sekwencji. Jeśli ostatni indeks zostanie pominięty, obejmuje wszystkie elementy aż do ostatniego elementu w sekwencji. Dozwolone są również indeksy ujemne. Skorzystaj z Google, aby dowiedzieć się więcej na ten temat.

batbrat
źródło
4
>>> import re

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

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

>>> re.match(p, path).group(1)
'c:\\path'
riza
źródło
1. Użyj r''ciągów znaków dla ścieżek Windows. 2. re.match()może zwrócić Brak
jfs
3

Kolejna prosta jedna linijka, o której tutaj nie wspomniano:

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

Będzie to również działać poprawnie w różnych przypadkach skrajnych:

>>> 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])
""
pR0Ps
źródło
2
line[5:]

daje znaki po pierwszych pięciu.

Steven Huwig
źródło
1

line[5:]da żądany podciąg. Przeszukaj wprowadzenie i poszukaj „notacji plasterków”

Pete Kirkham
źródło
1

Jeśli znasz listy składane:

lines = [line[5:] for line in file.readlines() if line[:5] == "Path="]
Matthew Schinckel
źródło
Nastąpiła zmiana sugerująca, że line.startswith(...)jest 10X szybsza. Moje testy tego nie potwierdziły. Chętnie to zmienię, jeśli zostaną przedstawione dowody potwierdzające to stwierdzenie.
Matthew Schinckel
0

Wersja pop nie była całkiem poprawna. Myślę, że chcesz:

>>> print('foofoobar'.split('foo', 1).pop())
foobar
fullung
źródło
0

Dlaczego nie użyć wyrażenia regularnego z funkcją ucieczki? ^dopasowuje początkową część wiersza i re.MULTILINEdopasowuje w każdym wierszu. re.escapezapewnia dokładne dopasowanie.

>>> print(re.sub('^' + re.escape('path='), repl='', string='path=c:\path\nd:\path2', flags=re.MULTILINE))
c:\path
d:\path2
Christoph Böddeker
źródło
0

Spróbuj poniższego kodu

if line.startswith("Path="): return line[5:]
dipenparmar12
źródło
1
Jaka jest różnica między Twoją odpowiedzią a zaakceptowaną odpowiedzią? Widzę, że znajduje się w pierwszej części drugiej odpowiedzi.
eyllanesc
-1

Myślę, że właśnie tego szukasz

    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
Pramod Bhat
źródło
-1

bez konieczności pisania funkcji, to podzieli się zgodnie z listą, w tym przypadku „Mr. W poniższym przypadku zwracany jest tekst „Morris”.

re.split('Mr.|Dr.|Mrs.', 'Mr. Morgan Morris')[1].split()[1]
xristian
źródło
-1

Jest to bardzo podobne pod względem techniki do innych odpowiedzi, ale bez powtarzających się operacji na łańcuchach, możliwość stwierdzenia, czy prefiks był, czy nie, i nadal całkiem czytelny:

parts = the_string.split(prefix_to_remove, 1):
    if len(parts) == 2:
        #  do things with parts[1]
        pass
kiwi
źródło