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.
- Hemmabyggt RISC-V-kluster når 14,6 GHz3
- Ljudproblem på ny dator från inet (nerkortad)53
- Fast i DRAM Training AMD 670E-Plus8
- Gamingdator ca 15k0
- Fanboy-quiz: Vad kan du om Sega?64
- Bästa komponenterna enligt SweClockers medlemmar52
- Köpråd 5.1 ljudsystem tv/dator4
- Råd om musikproducerande datorpaket11
- Garantiärende hos Webhallen slutar med att jag blir betalningsskyldig83
- Tråden om Nintendo Switch 217
- Säljes Div. äldre delar. i5 2400 med mobo samt 16Gb DDR3 och TX650
- Säljes Hyper 11 portars usb-c hub
- Säljes Dator utan diskar - 9900K, Z390 Hero Wifi, 32Gb, ASUS Strix 3070Ti
- Säljes Gigabyte GeForce RTX 2060 D6 Rev2 HDMI 3xDP 6GB
- Bytes Varmillo miya pro mx-brown mot samma/snarlik men mx-red.
- Säljes ASUS Dual RX 6750 XT OC
- Säljes ASUS ROG STRIX Z790-I GAMING WIFI
- Säljes Palit 3070ti gamerock och noctual nh-d15
- Säljes Zowie XL2546K 25" 240Hz
- Säljes 5900x + B550I PRO AX + SF750 + Kraken X63
- Hemmabyggt RISC-V-kluster når 14,6 GHz3
- Microsofts spelbutik för mobiler lanseras i sommar9
- Fullproppat uppdateringspaket på gång till Steam Deck16
- Krönika: Tanken av att prata med AI i spel gör mig deprimerad42
- Stack Overflow-användare ilsknar till efter Open AI-affär19
- Windows skärmdumpverktyg får QR‑kodläsare0
- Helgsnack: Kommer AMD kunna slå Nvidia igen?91
- Microsoft jobbar på ny widget-funktion i Start‑menyn26
- "Långsiktighet" är orsaken till Microsofts nedskärningar24
- Corsair visar upp ny blixtsnabb SSD23
Externa nyheter
Spelnyheter från FZ
- Det ryktas att Sony och Lego har ett samarbete på gång idag
- Hi-Fi Rush får en fysisk version trots att studion läggs ned idag
- Bungies Marathon går nu att ladda hem på Steam helt gratis igår
- AOC Agon Pro PD32M – 32 tum gamingmonitor igår
- Angry Video Game Nerd firar 20-årsjubileum med mer Castlevania igår