Comment supprimer la partie gauche d'une chaîne?

144

J'ai un code python simple qui recherche dans les fichiers une chaîne, par exemple path=c:\path, où la c:\pathpartie peut varier. Le code actuel est:

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

Quel est le moyen simple d'obtenir le texte après Path=?

grigoryvp
la source
Sachez que vous revenez sur la première occurrence de ligne du fichier qui commence par "Path =". D'autres réponses à ce message le font également. Mais si le fichier est quelque chose comme un fichier batch DOS, vous pouvez en fait vouloir la dernière occurrence de ligne d'un tel fichier selon que le "batch" ou le fichier de commandes n'est pas rempli de conditionnelles.
DevPlayer

Réponses:

25

À partir de Python 3.9, vous pouvez utiliser removeprefix:

'Path=helloworld'.removeprefix('Path=')
# 'helloworld'
Xavier Guihot
la source
6
voyager dans le temps beaucoup? ;-) de PEP 596 - Calendrier de publication de Python 3.9 : 3.9.0 final: Lundi, 05/10/2020
ssc
1
J'allais écrire la solution pour python 3.9 mais il semble que vous ayez mentionné les solutions python 3.9 partout. :)
Pygirl
196

Si la chaîne est fixe, vous pouvez simplement utiliser:

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

qui vous donne tout à partir de la position 5 dans la chaîne (une chaîne est aussi une séquence, donc ces opérateurs de séquence fonctionnent ici aussi).

Ou vous pouvez diviser la ligne au début =:

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

Alors param est "Path" et value est le reste après le premier =.

MrTopf
la source
3
+1 pour la méthode split, évite la légère laideur du tranchage manuel sur len (préfixe).
bobince
1
Mais jette aussi si votre entrée n'est pas tout sous la forme "quelque chose = autre chose".
Dan Olson
1
C'est pourquoi j'ai mis la condition devant afin qu'elle ne soit utilisée que si un "=" est dans la chaîne. Sinon, vous pouvez également tester la longueur du résultat de split () et si c'est == 2.
MrTopf
7
Comme le dit Dan Olson, splitlance une exception si le délimiteur n'est pas présent. partitionest plus stable, il divise également une chaîne et retourne toujours un tuple à trois éléments avec pré-, délimiteur et post-contenu (dont certains peuvent l'être ''si le délimiteur n'était pas présent). Par exemple, value = line.partition('=').
Anders Johansson
1
Split ne lève pas d'exception si le délimité n'est pas présent, il renvoie une liste avec la chaîne entière. Au moins sous python 2.7
Maxim
122

Supprimer le préfixe d'une chaîne

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

Fractionner à la première occurrence du séparateur 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

Analyser un fichier de type INI avec 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

Autres options

jfs
la source
1
Une raison rare pour indenter trois espaces au lieu de quatre.
Bob Stein
25
def remove_prefix(text, prefix):
    return text[len(prefix):] if text.startswith(prefix) else text
David Foster
la source
1
J'aime celui-ci parce que vous pouvez remplacer "else text" par "else False" ou "else None" ou tout autre type que vous voulez retourner pour indiquer que la ligne du fichier n'a pas commencé par "Path =". Personnellement, j'aime entourer mes opérateurs ternaires de parenthèses pour ressortir visuellement.
DevPlayer
19

Pour le tranchage (conditionnel ou non conditionnel) en général, je préfère ce qu'un collègue a suggéré récemment; Utilisez le remplacement par une chaîne vide. Plus facile à lire le code, moins de code (parfois) et moins de risque de spécifier le mauvais nombre de caractères. D'accord; Je n'utilise pas Python, mais dans d'autres langages, je préfère cette approche:

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

ou - pour donner suite au premier commentaire de cet article - si cela ne doit être fait que si la ligne commence par Path:

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

La principale différence avec certains de ce qui a été suggéré ci-dessus est qu'il n'y a pas de "nombre magique" (5) impliqué, ni aucun besoin de spécifier à la fois ' 5' et la chaîne ' Path=', En d'autres termes, je préfère cette approche à une maintenance de code point de vue.

fredarin
la source
Cela ne fonctionne pas: 'c = Path = a'.replace ("Path =", "", 1) ->' c = a '.
jfs
3
Cela ne répond pas à l'exigence d'origine de la chaîne commençant par "Path =".
Puppy
1
Vous pouvez remplacer le code regex par juste rightmost = re.sub('^Path=', '', fullPath). Le but de la compile()méthode est d'accélérer les choses si vous réutilisez l'objet compilé, mais puisque vous le jetez après l'avoir utilisé, cela n'a aucun effet ici de toute façon. De toute façon, cela ne vaut généralement pas la peine de s'inquiéter de cette optimisation.
Jim Oldfield
13

Je préfère popindexer [-1]:

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

à

value = line.split("Path=", 1)[1]
param, value = line.split("Path=", 1)
Thomas Schreiber
la source
2
Belle alternative sans "nombres magiques". Il est intéressant de noter que cela fonctionne parce que cela startswitha déjà été testé, donc splitne divisera «rien» avant et tout le reste après. split("Path=", 1)est plus précis (en cas de réapparition du préfixe plus tard dans la chaîne) mais réintroduit un nombre magique.
quornian
1
Version plus courte du commentaire précédent (très important): cela fonctionne UNIQUEMENT si vous testez d'abord avec startswith ().
MarcH
12

Ou pourquoi pas

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

Que diriez-vous..

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

Ce triplet est la tête, le séparateur et la queue .

Floggedhorse
la source
Cela ne fonctionne pas dans tous les cas de la même manière. Si le séparateur est présent, le résultat est le troisième élément. Sinon, le résultat est le premier élément.
Ioannis Filippidis
5

Le moyen le plus simple auquel je puisse penser est le tranchage:

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

Une note rapide sur la notation des tranches, il utilise deux indices au lieu de l'habituel. Le premier index indique le premier élément de la séquence que vous souhaitez inclure dans la tranche et le dernier index est l'index immédiatement après le dernier élément que vous souhaitez inclure dans la tranche.
Par exemple:

sequence_obj[first_index:last_index]

La tranche se compose de tous les éléments entre first_indexet last_index, y compris first_indexet non last_index. Si le premier index est omis, il prend par défaut le début de la séquence. Si le dernier index est omis, il inclut tous les éléments jusqu'au dernier élément de la séquence. Les indices négatifs sont également autorisés. Utilisez Google pour en savoir plus sur le sujet.

batbrat
la source
4
>>> import re

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

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

>>> re.match(p, path).group(1)
'c:\\path'
riza
la source
1. Utilisez des r''chaînes pour les chemins Windows. 2. re.match()peut retourner None
jfs
3

Un autre simple one-liner qui n'a pas été mentionné ici:

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

Cela fonctionnera également correctement pour divers cas extrêmes:

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

vous donne des caractères après les cinq premiers.

Steven Huwig
la source
1

line[5:]donnera la sous-chaîne souhaitée. Recherchez l' introduction et recherchez la `` notation de tranche ''

Pete Kirkham
la source
1

Si vous connaissez la compréhension des listes:

lines = [line[5:] for line in file.readlines() if line[:5] == "Path="]
Matthew Schinckel
la source
Il y a eu une modification suggérant qu'il line.startswith(...)est 10 fois plus rapide. Mes tests ne l'ont pas confirmé. Heureux de le changer si des preuves étayant cette affirmation sont fournies.
Matthew Schinckel
0

La version pop n'était pas tout à fait correcte. Je pense que tu veux:

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

Pourquoi ne pas utiliser regex avec escape? ^correspond à la partie initiale d'une ligne et re.MULTILINEcorrespond à chaque ligne. re.escapegarantit que la correspondance est exacte.

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

Essayez le code suivant

if line.startswith("Path="): return line[5:]
dipenparmar12
la source
1
Quelle est la différence entre votre réponse et la réponse acceptée? Je vois que c'est dans la première partie de l'autre réponse.
eyllanesc
-1

Je suppose que c'est exactement ce que tu cherches

    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
la source
-1

sans avoir à écrire une fonction, cela sera divisé en fonction de la liste, dans ce cas «M. | Dr. | Mme.», sélectionnez tout après la séparation avec [1], puis divisez à nouveau et prenez n'importe quel élément. Dans le cas ci-dessous, «Morris» est renvoyé.

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

Cette technique est très similaire à celle des autres réponses, mais sans opérations répétées sur les chaînes, possibilité de dire si le préfixe était là ou non, et toujours assez lisible:

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