Como remover a parte esquerda de uma string?

144

Eu tenho algum código python simples que pesquisa arquivos por uma string path=c:\path, por exemplo , onde a c:\pathparte pode variar. O código atual é:

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

Qual é uma maneira simples de obter o texto depois Path=?

grigoryvp
fonte
Esteja ciente de que você está retornando à ocorrência da primeira linha no arquivo que começa com "Path =". Outras respostas para este post também o fazem. Mas se o arquivo for algo parecido com um arquivo em lotes do DOS, você poderá realmente querer a ocorrência da última linha desse arquivo, dependendo se o "lote" ou o arquivo de comando não for preenchido com condicionais.
DevPlayer 27/08/16

Respostas:

22

Começando em Python 3.9, você pode usar removeprefix:

'Path=helloworld'.removeprefix('Path=')
# 'helloworld'
Xavier Guihot
fonte
4
viajar no tempo muito? ;-) do PEP 596 - Python 3.9 Horário de lançamento : 3.9.0 final: segunda-feira, 2020-10-05
ssc
Eu ia escrever a solução para o python 3.9, mas parece que você mencionou as soluções do python 3.9 em todos os lugares. :)
Pygirl 17/06
196

Se a string for fixa, você pode simplesmente usar:

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

que fornece tudo da posição 5 na sequência (uma sequência também é uma sequência, portanto esses operadores de sequência também funcionam aqui).

Ou você pode dividir a linha no primeiro =:

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

Então param é "Caminho" e valor é o restante após o primeiro =.

MrTopf
fonte
3
+1 para o método de divisão, evita a ligeira feiura do fatiamento manual em len (prefixo).
9990
1
Mas também lança se sua entrada não estiver toda na forma "algo = algo mais".
1111 Dan Olson
1
É por isso que coloco a condição na frente, para que seja usada apenas se um "=" estiver na string. Caso contrário, você também pode testar o comprimento do resultado de split () e se é == 2.
214 MrTopf
7
Como Dan Olson diz, splitlança uma exceção se o delimitador não estiver presente. partitioné mais estável, também divide uma string e sempre retorna uma tupla de três elementos com pré, delimitador e pós-conteúdo (alguns dos quais podem ser ''se o delimitador não estava presente). Por exemplo value = line.partition('='),.
Anders Johansson
1
Split não lança uma exceção se o delimitado não estiver presente, ele retorna uma lista com toda a string. Pelo menos em python 2.7
Maxim
122

Remover prefixo de uma sequência

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

Divida na primeira ocorrência do separador via 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

Analisar arquivo semelhante ao INI com o 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

Outras opções

jfs
fonte
1
Um motivo raro para recuar três espaços em vez de quatro.
Bob Stein
25
def remove_prefix(text, prefix):
    return text[len(prefix):] if text.startswith(prefix) else text
David Foster
fonte
1
Eu gosto deste porque você pode substituir "else text" por "else False" ou "else None" ou qualquer outro tipo que você queira retornar para indicar que a linha no arquivo não começou com "Path =". Pessoalmente, gosto de cercar meus operadores ternários com parênteses para me destacar visualmente.
DevPlayer
19

Para fatiar (condicional ou não condicional) em geral, prefiro o que um colega sugeriu recentemente; Use a substituição por uma string vazia. Mais fácil de ler o código, menos código (às vezes) e menos risco de especificar o número errado de caracteres. Está bem; Eu não uso Python, mas em outras línguas prefiro esta abordagem:

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

ou - para acompanhar o primeiro comentário deste post - se isso for feito apenas se a linha começar com Path:

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

A principal diferença em relação ao que foi sugerido acima é que não há "número mágico" (5) envolvido, nem há necessidade de especificar ' 5' e a string ' Path='. Em outras palavras, prefiro essa abordagem a partir de uma manutenção de código ponto de vista.

fredarin
fonte
Não funciona: 'c = Caminho = a'.replace ("Path =", "", 1) ->' c = a '.
JFS
3
Isso não atende ao requisito original da sequência que começa com "Path =".
Filhote de cachorro
1
Você pode substituir o código regex com just rightmost = re.sub('^Path=', '', fullPath). O objetivo do compile()método é tornar as coisas mais rápidas se você reutilizar o objeto compilado, mas como você o joga fora depois de usá-lo, ele não tem efeito aqui. Geralmente, não vale a pena se preocupar com essa otimização.
Jim Oldfield
13

Eu prefiro popindexar [-1]:

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

para

value = line.split("Path=", 1)[1]
param, value = line.split("Path=", 1)
Thomas Schreiber
fonte
2
Boa alternativa sem "números mágicos". Vale a pena notar que isso funciona porque startswithjá foi testado e, portanto split, dividirá "nada" antes e tudo o mais depois. split("Path=", 1)é mais preciso (no caso do prefixo reaparecer mais tarde na sequência), mas reintroduz um número mágico.
quornian
1
Versão mais curta do comentário anterior (muito importante): isso funciona SOMENTE se você testar primeiro o begin com ().
13133 MarcHam
12

Ou porque não

if line.startswith(prefix):
    return line.replace(prefix, '', 1)
John Damen
fonte
5

E se..

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

Esse trigêmeo é a cabeça, o separador e a cauda .

Cavalo de aço
fonte
Isso não funciona em todos os casos da mesma maneira. Se o separador estiver presente, o resultado será o terceiro item. Caso contrário, o resultado é o primeiro item.
Ioannis Filippidis
5

A maneira mais simples de pensar é com o fatiamento:

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

Uma observação rápida na notação de fatia, usa dois índices em vez do habitual. O primeiro índice indica o primeiro elemento da sequência que você deseja incluir na fatia e o último índice é o índice imediatamente após o último elemento que você deseja incluir na fatia.
Por exemplo:

sequence_obj[first_index:last_index]

A fatia consiste em todos os elementos entre first_indexe last_index, incluindo first_indexe não last_index. Se o primeiro índice for omitido, o padrão será o início da sequência. Se o último índice for omitido, ele incluirá todos os elementos até o último elemento na sequência. Índices negativos também são permitidos. Use o Google para saber mais sobre o tópico.

morcego
fonte
4
>>> import re

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

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

>>> re.match(p, path).group(1)
'c:\\path'
riza
fonte
1. Use r''cadeias de caracteres para caminhos do Windows. 2. re.match()pode retornar None
jfs
3

Outra linha simples que não foi mencionada aqui:

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

Isso também funcionará corretamente para vários casos extremos:

>>> 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
fonte
2
line[5:]

dá-lhe caracteres após os cinco primeiros.

Steven Huwig
fonte
1

line[5:]dará a substring que você deseja. Pesquise a introdução e procure por 'notação de fatia'

Pete Kirkham
fonte
1

Se você conhece as compreensões da lista:

lines = [line[5:] for line in file.readlines() if line[:5] == "Path="]
Matthew Schinckel
fonte
Houve uma edição sugerida line.startswith(...)10 vezes mais rápida. Meus testes não confirmaram isso. É um prazer alterá-lo se forem fornecidas evidências que sustentam essa afirmação.
Matthew Schinckel
0

A versão pop não estava certa. Eu acho que você quer:

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

Por que não usar regex com escape? ^corresponde à parte inicial de uma linha e re.MULTILINEcorresponde a cada linha. re.escapegarante que a correspondência seja exata.

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

Experimente o código a seguir

if line.startswith("Path="): return line[5:]
dipenparmar12
fonte
1
Qual a diferença entre sua resposta e a resposta aceita? Vejo que está na primeira parte da outra resposta.
eyllanesc
-1

Eu acho que é isso que você está procurando exatamente

    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
fonte
-1

sem ter que escrever uma função, isso será dividido de acordo com a lista, neste caso 'Sr. | Dr. | Sra.', selecione tudo após dividir com [1], depois divida novamente e pegue qualquer elemento. No caso abaixo, 'Morris' é retornado.

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

Isso é muito semelhante na técnica a outras respostas, mas sem operações repetidas de cadeias, capacidade de saber se o prefixo estava lá ou não, e ainda assim é legível:

parts = the_string.split(prefix_to_remove, 1):
    if len(parts) == 2:
        #  do things with parts[1]
        pass
kiwi
fonte