Hoe gebruik ik regex bij het selecteren van een tekstdeel?

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
Ik wil met python bepaalde tekstdelen uit een txt bestand halen. Uit een eerdere topic is mij geadviseerd daarvoor met regex te experimenteren. Dat blijkt niet zo eenvoudig.
Het is mij wel al gelukt een regex string te maken die een match geeft op alle tekstdelen. Het gaat er nu om de juiste bijbehorende python code te kiezen waarmee ik een specifiek deel uit dit tekstbestand kan ophalen om dit in een ander bestand te schrijven. Ik heb hiervoor een oefenbestand gemaakt. In dit tekstbestand staan tekstdelen die gekenmerkt worden door een begincode en een eindcode. De begincode luidt: "* 00" of "*01" of "* 02" enz. Al deze tekstdelen eindigen met een enkele "*".

Het bestand ziet er als volgt uit:
=============================================
* 00
Dit is the inleiding.
*
* 01
Dit is deel 1. Dit is deel 1.Dit is deel 1. Dit is deel 1.
Dit is deel 1. Dit is deel 1.
*
* 02
Dit is deel 2. Dit is deel 2. Dit is deel 2. Dit is deel 2. Dit is deel 2. Dit is deel 2.
Dit is deel 2. Dit is deel 2. Dit is deel 2. Dit is deel 2. Dit is deel 2.
8
* 03
Dit is deel 3. Dit is deel 3. Dit is deel 3. Dit is deel 3. Dit is deel 3. Dit is deel 3. Dit is deel 3.
Dit is deel 3. Dit is deel 3. Dit is deel 3. Dit is deel 3.
*
* 11
Dit is deel 11. Dit is deel 11. Dit is deel 11. Dit is deel 11.
Dit is deel 11.
*
* 33
Dit is deel 33
*
=============================================

De regex string die ik gevonden heb, ziet er als volgt uit: \*\s?\d+[\n](.+\n)(\n?\*)

Mijn vraag is nu: Hoe zet ik een python code op waarmee ik bijvoorbeeld
de hele tekst "Dit is deel 3 .............." wil opvragen?
Ik weet dat ik het commando re.findall() bij "lines" moet gebruiken, maar al mijn pogingen stranden hier tot nu. Ik ben het spoor bijster.

Wie kan mij op weg helpen? _/-\o_

Acties:
  • +1 Henk 'm!

  • skate master
  • Registratie: September 2004
  • Laatst online: 08:43

skate master

Autodesk Educator Expert

Ik zou een array maken van de regex output.
Vervolgens kun je dan vrij eenvoudig bijvoorbeeld het 3e item weergeven.

Acties:
  • 0 Henk 'm!

  • DHH
  • Registratie: Augustus 2014
  • Laatst online: 07-09-2024

DHH

Jokiehints schreef op zaterdag 2 maart 2019 @ 19:54:
De begincode luidt: "* 00" of "*01" of "* 02" enz. Al deze tekstdelen eindigen met een enkele "*".
...
De regex string die ik gevonden heb, ziet er als volgt uit: \*\s?\d+[\n](.+\n)(\n?\*)
Even zeuren ;) in je tekst geef je aan dat de begincode * 00, *01 of * 02 is: die (optionele) spatie is voor de regex wel van belang: het vraagteken achter \s bepaalt of deze optioneel is. In je voorbeeld komt deze spatie altijd voor, dus als deze altijd voorkomt, dan zou ik het vraagteken er ook uithalen.

Verder zou ik even je code tot zover posten en waar je precies tegenaan loopt, want je hebt het nu over 'lines', maar zonder het vorige topic te kennen, zegt 'lines' niet zo heel veel (en we weten niet wat je er sinds het vorige topic van gebakken hebt).

Acties:
  • 0 Henk 'm!

  • CyBeRSPiN
  • Registratie: Februari 2001
  • Laatst online: 10:29

CyBeRSPiN

sinds 2001

Met sed kan het als volgt, moet het perse in Python?
code:
1
$ sed -n '/\* 03/,/\*/p' file


https://unix.stackexchange.com/a/236754

Acties:
  • 0 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
@CyBeRSPiN Ik ben een beginner en ben bezig mij in python te verdiepen. Van sed weet ik niets. Volgens wiki betreft het een 'unix utilitie'. Omdat ik nieuw ben op programmeer gebied weet ik niet of en zo ja hoe ik sed kan gebruiken. Ik zoek nu een oplossing in python en de bibliotheken die ik vanuit python kan oproepen. Ik hoor graag van jou of ik sed daarbij kan gebruiken.

Acties:
  • 0 Henk 'm!

  • CyBeRSPiN
  • Registratie: Februari 2001
  • Laatst online: 10:29

CyBeRSPiN

sinds 2001

@Jokiehints ok, dan heb ik niks gezegd. Het is inderdaad een programma dat altijd in Linux aanwezig is en veel wordt gebruikt in scripts.
Met Python moet het absoluut ook lukken, maar dan moet je hier wat meer delen van je script en je output.

Acties:
  • 0 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
@DHH Ja, de spatie komt overal voor. Ik kan deze weghalen. De regex code moet ik dan aanpassen.
De code waar ik op vast loop is re.findall().

Met de volgende code lees ik mijn hele txt bestand met alle tekstdelen uit.
Deze stop ik in de variabele "line".
Python:
1
2
3
4
5
6
filename = 'test_file.txt'  

with open(filename, "r", encoding='utf-8', errors='ignore') as file_object:      
line in file_object:
    print(line)         # het print(line) wordt alles per aparte regel weergegeven                           
    #print(line.rstrip())   # rstrip() zorgt ervoor dat de witregel na elke regel verdwijnt.
Daarna print ik het hele bestand uit.
Python:
1
print(line)
Op de site van python.org staat info over de functie waar jij mij eerder op attendeerde.
https://docs.python.org/3...ght=re.findall#re.findall
Python:
1
re.findall(pattern, string, flags=0)
Het gaat mij er nu om hoe ik de re.findall() functie verder moet invullen om het tekstdeel achter "*03" in de print functie te krijgen. Met andere woorden: wat vul ik in bij 'pattern' en 'flags'? Ik neem aan dat ik bij 'string' 'line' moet invullen? Weet jij hoe nu verder?

[ Voor 6% gewijzigd door Jokiehints op 03-03-2019 11:45 ]


Acties:
  • 0 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
@CyBeRSPiN Zie mijn reactie aan @DHH

Acties:
  • 0 Henk 'm!

  • krvabo
  • Registratie: Januari 2003
  • Laatst online: 13-09 17:39

krvabo

MATERIALISE!

De documentatie vertelt opzich wat pattern, string en flags zijn.
Pattern is de regex, string is de inhoud van je tekstbestand en flags zijn opties (die je niet op hoeft te geven) die je regex 'veranderen'. Als voorbeelden: dotAll, Multiline en case insensitive (ignorecase). Zo kun je bijvoorbeeld [a-z] ook hoofdletters laten matchen, of een punt (.) ook new lines laten matchen.

Ik denk dat je voor je probleem op zoek bent naar capture groups.
Uit de documentatie die je linkt:
(...)
Matches whatever regular expression is inside the parentheses, and indicates the start and end of a group; the contents of a group can be retrieved after a match has been performed, and can be matched later in the string with the \number special sequence, described below. To match the literals '(' or ')', use \( or \), or enclose them inside a character class: \[(], \[)].
Deze values zijn terug te halen met de group() functie (match.group(1) bijvoorbeeld).

Pong is probably the best designed shooter in the world.
It's the only one that is made so that if you camp, you die.


Acties:
  • 0 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
@krvabo Bedankt voor je reactie. Ik voel dat ik de goede kant op wordt geduwd.
Je bedoelt de capture groups zoals deze geformuleerd zijn in de regex string?
De regex string is de string die volgens mij in het tekstbestand nagaat of daar (stukjes tekst) staan die voldoen aan de voorwaarden van de capture groups.
De functies group() en match.group() moeten mij helpen die specifieke waarden op te geven om dat specifieke stukje tekst eruit te lichten. Daar gaat het om. Die begin waarde (bijvoorbeeld "* 03") en de eindwaarde (*) moet ik ergens formuleren, anders krijg ik nooit dat bewuste stukje tekst uit mijn tekstbestand. Maar hoe doe ik dat? Heb je ergens een voorbeeld dat ik kan gebruiken?

Acties:
  • +1 Henk 'm!

  • krvabo
  • Registratie: Januari 2003
  • Laatst online: 13-09 17:39

krvabo

MATERIALISE!

Uitgaande van de regex die je hebt gepost: \*\s?\d+[\n](.+\n)(\n?\*)

Daarbij is (.+\n) een capture group, en is (\n?\*) een capture group.
Wil je het nummer los, en de tekst los dan wordt je regex iets als dit: \*\s?(\d+)\n(.+\n)\n?\*
In je originele regex staat \n tussen blokhaken, wat niet nodig is, en je capture groups zijn daar ook niet nodig als je niet juist die tekst wil hebben. (Je regex werkt ook niet goed trouwens, alles staat in 1 capture).

Als je formaat altijd vast staat qua spaties dan kan ie zelfs gewoon zo: \* (\d+)\n(.+?)\n\* met de DotAll flag. Ik heb deze regex getest en deze doet exact wat je wil. Als je de regex uitvoert, dan krijg je een array/list met matches terug. Loop over de matches en gebruik group(1) voor het nummer, en group(2) voor de tekst.

Er zijn vrij veel websites waar je online je regexen kunt testen, zoals https://regexr.com/ . Gooi de regex daar in, en klik dan onderaan op details. Als je een match aanklikt (zo'n blauw) vak, dan zie je uitleg over de groups.

Pong is probably the best designed shooter in the world.
It's the only one that is made so that if you camp, you die.


Acties:
  • 0 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
_/-\o_ @krvabo Super bedankt voor je hulp. Hier ga ik verder mee. d:)b

Acties:
  • 0 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
@krvabo De regex string) '\*\s?(\d+)\n(.+\n)\n?' doet precies wat deze moet doen. In mijn txt bestand heb ik alle mogelijke matches staan. En dit zonder de asterix. groep(1) en groep(2) worden zo zichtbaar. Prachtig hoe dit zichtbaar wordt

Ik heb nu een programmaatje gecomponeerd waarmee ik een deel uit deze tekstbrij wil halen en printen. ALS zoekcode voor group(1)
Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
import re

filename = 'test_file.txt'  
zoekstring = "03"

pattern = '\*\s?(\d+)\n(.+\n)\n?'

a = re.search(pattern, filename)
for tekst in a:
    if a.group(1) == zoekstring:
        print(a.group(2))
    else:
        print("niet gevonden")
Helaas krijg ik hier een foutmelding
Python:
1
2
3
4
Traceback (most recent call last):
  File "/Users/jokiehints/Library/Mobile Documents/com~apple~CloudDocs/python_files/num_files/num_test_reader_pag2.py", line 26, in <module>
    for tekst in a:
TypeError: 'NoneType' object is not iterable
Kan jij zien wat hier fout gaat of moet ik het anders aanpakken?

Acties:
  • 0 Henk 'm!

  • krvabo
  • Registratie: Januari 2003
  • Laatst online: 13-09 17:39

krvabo

MATERIALISE!

Als ik je regex in regexr gooi (die ik eerder linkte) dan is daar zichtbaar dat alleen de eerste zin uit zo'n tekstgroep wordt geselecteerd (i.t.t. de regex die ik als laatste gaf), fyi.

Ik ben niet bekend met Python, maar als ik zo moet gokken dan is 'a' geen iterabel (= loop-baar) object. M.a.w. wat je krijgt uit de search() functie is geen lijst met resultaten. Volgens mij moet je ook "if tekst.group(1)" gebruiken ipv "a.group(1)" omdat 'a' alle resultaten bevat, i.t.t. tekst, wat een van de resultaten is van de regex. Maar aangezien ik geen ervaring heb met Python durf ik dat niet met zekerheid te zeggen. Kwestie van debuggen lijkt me. Print je variabelen en kijk wat het is.

Pong is probably the best designed shooter in the world.
It's the only one that is made so that if you camp, you die.


Acties:
  • +1 Henk 'm!

  • Eewokney
  • Registratie: September 2012
  • Laatst online: 29-08 08:59
Probleem is dat je variable filename nu de text 'test_file.txt' is, niet de inhoud van het bestand.

Ik ben zelf niet zo bekend met regex gebruiken in python. heb deze en deze site gebruikt en tot het volgonde gekomen:

Python: num_test_reader_pag2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import re

def read_file(file_name):
    """ opens a file and returns its input as a string."""
    with open(file_name, 'r') as selected_file:
        return selected_file.read()

filename = 'test_file.txt'
zoekstring = "03"
pattern = r'\*\s?(\d+)\n(.+\n)\n?'

b = read_file(filename)
# print(b)
a = re.findall(pattern, b)

for tekst in a:
    # print(tekst)
    if zoekstring in tekst:
        print(tekst[1])

volgends de eerste site moet er ook een r voor de pattern:
Do you notice the r at the start of the pattern Cookie?

This is called a raw string literal. It changes how the string literal is interpreted. Such literals are stored as they appear.

For example, \ is just a backslash when prefixed with a r rather than being interpreted as an escape sequence
Ik heb de re.findall functie gebruikt zodat je een list krijgt waar je over kan itereren.

[ Voor 4% gewijzigd door Eewokney op 03-03-2019 18:09 ]


Acties:
  • 0 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
@Eewokney Ja deze code werkt en doet precies wat mijn bedoeling is. bedankt voor je meedenken.
Ik heb alleen een kleine toevoeging moeten doen in de 'with open' regel.
Python:
1
with open(file_name, 'r', encoding='utf-8', errors='ignore') as selected_file:
Ik kreeg namelijk een foutmelding op utf.
Python:
1
2
3
4
5
6
7
8
9
Traceback (most recent call last):
  File "/Users/jokiehints/Library/Mobile Documents/com~apple~CloudDocs/python_files/num_files/num_test_reader_pag3.py", line 12, in <module>
    b = read_file(filename)
  File "/Users/jokiehints/Library/Mobile Documents/com~apple~CloudDocs/python_files/num_files/num_test_reader_pag3.py", line 6, in read_file
    return selected_file.read()
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/codecs.py", line 322, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x91 in position 4861: invalid start byte
[Finished in 0.0s with exit code 1]
Daarna werkte het als een speer! Top _/-\o_ _/-\o_

Acties:
  • +1 Henk 'm!

  • Jokiehints
  • Registratie: Februari 2019
  • Laatst online: 02-04-2024
@CyBeRSPiN Bedankt voor je input. Ik heb hierdoor veel geleerd. @Eewokney heeft uiteindelijk de oplossing gegeven. Ik kan nu verder met mijn programma d:)b
Pagina: 1