Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.
[Python] Funktion som kan returnera index av en viss karaktär från en sträng?
Du kan exempelvis använda strängens find
-metod:
find(...)
S.find(sub[, start[, end]]) -> int
Return the lowest index in S where substring sub is found,
such that sub is contained within S[start:end]. Optional
arguments start and end are interpreted as in slice notation.
Return -1 on failure.
(Se även help(str.index)
.)
och hitta indexen genom:
>>> def find_character_indices(haystack, needle):
... indices = []
... i = -1
... while True:
... i = haystack.find(needle, i + 1)
... if i == -1:
... break
... indices.append(i)
... return indices
...
>>> find_character_indices('oslo', 'o')
[0, 3]
Dock ser detta inte speciellt "Pythonskt" ut (i en viss pedagogisk medvetenhet från min sida; "There must be a better way!" (länk till föredraget "Beyond PEP-8" av Raymond Hettinger från PyCon 2015 — rekommenderad tittning)). Liknande loopar med förinitialiserade variabler kan allt som oftast skrivas bättre (mer läsbart) som en "list comprehension" i stället.
En sträng i Python är en sekvens (av tecken), och sekvenser är iterabla. enumerate()
tar en iterabel och mappar varje element till ett sekventiellt numeriskt index. Testet för de index vi vill "spara" är enkelt: vi vill spara de index som hör till ett tecken som matchar det vi söker. Vi kan skriva det i stort sett som en "one-liner" med just en "list comprehension":
>>> def find_character_indices(haystack, needle):
... return [i for i, character in enumerate(haystack) if character == needle]
...
>>> find_character_indices('oslo', 'o')
[0, 3]
Byter man []
mot ()
så får man en generator i stället för en lista, vilket är mer naturligt om man ändå tänkt iterera över indexen i ett senare tillfälle (och väldigt användbart om man vill förbereda sig på gigantiska "haystack"-strängar).
För att hämta exempelvis den andra förekomsten av ett tecken i en sträng så är det bara att fråga efter det andra elementet i den returnerade listan (skrevs funktionen som en generator kan man konvertera generatorn till en lista med list()
, men om man bara tänkt använda den som en lista är det ju smidigare att låta funktionen returnera en sådan).
Det går även att lösa detta genom reguljära uttryck, men det är som att skjuta myggor med kanoner .
@phz: Jag förstod inte riktigt allt men jag förstod din funktion. Har inte hållet på med programmering så länge (jag är 14). Jag hade fått för mig att det fanns en inbyggd funktion som gör detta och tyckte det kändes onödigt att skriva den själv. Men nu är allt löst, tack
Det är fritt fram att fråga gällande saker som var oklara om du undrar över något. Speciellt "list comprehensions" känns rätt magiska första gångerna man ser dem, men de beskriver ofta algoritmer på ett väldigt enkelt sätt, och bör användas där de passar.
Det kan kännas som onödigt mycket "jobb" att läsa in sig på hur en lösning fungerar när den väl gör vad du vill, men just för att det är ett så enkelt exempel så är det extra lärorikt att se till att man har full koll på vad som händer. I mer komplexa exempel så blir det mycket svårare att börja bena i koden.
Jag vet ingen direkt funktion i Pythons standardbibliotek som gör exakt det du frågar efter. Så som jag skrev min funktion så returnerar den en lista, ur vilken man sedan kan välja index enligt "vanlig" listsyntax.
Man skulle alternativt kunna skapa en funktion som tog ett ordningstal som ett extra argument och direkt returnerade ett index, men generellt är det trevligt att försöka skriva Python-kod som använder de mekanismer som språket erbjuder, vilket inkluderar []
-konstruktionen för att välja listelement.
Om man letar efter den andra förekomsten av tecknet 'o'
i strängen 'oslo'
så skulle skillnaden mellan två sådana lösningar vara, om jag först upprepar den jag skrev ovan:
>>> def find_character_indices(haystack, needle):
... return [i for i, character in enumerate(haystack) if character == needle]
...
>>> find_character_indices('oslo', 'o')[1]
3
där vi använder listsyntaxen på den lista av träffar som returnerats. Nu den andra varianten:
>>> def find_character_index(haystack, needle, n):
... return [i for i, character in enumerate(haystack) if character == needle][n - 1]
...
>>> find_character_index('oslo', 'o', 2)
3
(notera den lilla skillnaden i funktionsnamn) där man alltså ger ordningstalet (2
för "den andra") för den träff man vill få indexet för.
Än enklare så skulle man kunna erbjuda båda alternativen och låta den andra varianten ärva funktion från den första:
>>> def find_character_indices(haystack, needle):
... return [i for i, character in enumerate(haystack) if character == needle]
...
>>> def find_character_index(haystack, needle, n):
... return find_character_indices(haystack, needle)[n - 1]
...
>>> find_character_index('oslo', 'o', 2)
3
Måhända överdriven omfaktorering här, men det kan ofta vara trevligt att låta varje funktion sköta "så lite som möjligt" på egen hand, så att de alla är fullkomligt självklar att beskriva (och testa!). När man skriver på detta sätt så ser man också tydligt att den andra funktionen, vars enda uppgift är att plocka ut ett listindex, känns rätt blek och att man troligen klarar sig utan den. Koden i den första funktionen känns vettigare att ha i en återanvändbar funktion.
Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.
@phz: Nu förstår jag "oneline" koden bättre. Listor är förvirrande tycker jag, börjar på 0 och det glömmer jag ibland. Men de är riktigt bra när jag får de att funka :).
En bit av ett program jag håller på med ska ta en sträng och sortera ut allt som ligger mellan newLine tecknens ("\n") och lägga de i en lista. De var det jag ville ha funktionen som du skrev till (Den är snyggt skriven ). Så tack för hjälpen, nu har jag lärt mig något nytt.
@phz: Nu förstår jag "oneline" koden bättre. Listor är förvirrande tycker jag, börjar på 0 och det glömmer jag ibland. Men de är riktigt bra när jag får de att funka :).
En bit av ett program jag håller på med ska ta en sträng och sortera ut allt som ligger mellan newLine tecknens ("\n") och lägga de i en lista. De var det jag ville ha funktionen som du skrev till (Den är snyggt skriven ). Så tack för hjälpen, nu har jag lärt mig något nytt.
Med den mer specifika beskrivningen så kan du enklare titta på split
, eller kanske än mer splitlines
:
>>> 'Never gonna give you up\nNever gonna let you down\nNever gonna run around and desert you'.splitlines()
['Never gonna give you up', 'Never gonna let you down', 'Never gonna run around and desert you']
Ifall det du läser "strömmas" från exempelvis en fil så finns det mer direkta sätt att läsa rad för rad. Om vi antar att vi har en fil astley
med samma text som teststrängen ovan så kan du antingen läsa in hela filen i en lista där varje rad blir ett element med
>>> with open('astley') as f:
... lines = f.readlines()
...
>>> lines
['Never gonna give you up\n', 'Never gonna let you down\n', 'Never gonna run around and desert you\n']
(notera att radbrytningen är en del av varje element; se nedan för ett sätt att strippa den) eller så kan du läsa filen sekventiellt och agera på varje rad efterhand:
>>> with open('astley') as f:
... for line in f:
... print('[RA]: {}'.format(line.rstrip('\n')))
...
[RA]: Never gonna give you up
[RA]: Never gonna let you down
[RA]: Never gonna run around and desert you
där jag använde rstrip
för att rensa bort den avslutande extra radbrytningen för varje rad (beroende på exakt vad man vill göra kanske man vill sköta detta annorlunda).
Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.
- 2 Studio monitors till PC7
- Fanboy-quiz: Vad kan du om Microsoft?28
- Dagens fynd — Diskussionstråden49872
- SweCyklers1570
- Året var 2003 – Innan SSD:n vandrade på jorden fanns Raptorn74
- Tråden om Xbox Series X|S7611
- Dagens fynd (bara tips, ingen diskussion) — Läs första inlägget först!18596
- MSI 3070Ti -Fläktarna går 100%7
- Falska VPN-program spred skadlig kod i botnät under tio års tid8
- SweSpacers - tråden om rymden255
- Säljes Logitech G560 halva nypriset
- Säljes MSI B550M PRO-VDH WIFI Defekt?
- Säljes SteelSeries Apex 7 Blue Switch
- Köpes Söker gamingdator till sonen
- Säljes Fractal Design Torrent TG x EKWB Quantum radiator
- Säljes Asus 3060, Gainward 1660Super, Ryzen 3600, LianLi 205M
- Säljes Samsung SSD870 EVO 2TB / 1TB 870 EVO Oöppnade
- Säljes Reciever Onkyo TX-NR626, reparationsex/reservdelsex. Komplett men trasig, missljud. Går att laga?
- Säljes AMD Ryzen 5 2600X 3.6 GHz 19MB (AM4) med tillhörande fläkt (Wraith Spire).
- Säljes HP Probook 440 G8 14"/i5-1135G7/8GB/240GB/Geforce MX450/Garanti 2025
- Falska VPN-program spred skadlig kod i botnät under tio års tid8
- Fanboy-quiz: Vad kan du om Microsoft?28
- Enkelt knep öppnar gamla Utforskaren i Windows 1114
- Microsoft återstartar Windows 10-betatester20
- Seasonic samarbetar med Noctua på specialaggregat27
- Duellen: Premium-kylare vs. budgetfavorit22
- Apple uppgav fel antal kärnor för Ipad Airs gpu18
- Fractal visar upp stilrena stolar och hörlurar24
- Fractal släpper chassi med textil61
- Veckans fråga: Stylar du ditt chassi?40