nav_dugme codeBlog codeBlog
  • početna Početna stranica
  • Sačuvani članci Sačuvani članci
  • Članci
     (spisak)
  • Kontakt
Povratak na vrh stranice

Info & povezani članci Info o članku - dugme

Info

trejler_sat Datum objave: 27.04.2021.

trejler_dokument Jezici: Python

trejler_teg_narandzasti Težina: 6/10

Python
oop
unicode
algoritam
datoteke
editori
obrada teksta
Sublime Text
Vim
tutorijali
saveti
zanimljivosti

Povezani članci

Uvod u PythonŠablonske niske u programskim jezicimaOperacije sa tekstualnim datotekama u programskim jezicima C i PythonUpotreba specijalnih znakova u HTML datotekamaKlase složenosti algoritama (O notacija)Strukture podatakaIzbor prvog programskog jezikaKako napraviti syntax highlighterASCII, Unicode i UTF - Predstavljanje znakova na računarimaGNU/Linux - 1. deo - Uvod
Svi članci
First, solve the problem. Then, write the code.
John Johnson

Tutorijal - Implementacija markup jezika u Python-u

Facebook LinkedIn Twitter Viber WhatsApp E-mail
zoom_plus zoom_minus bookmark
početna > Članci > Saveti

Uvod

Pošto smo se upoznali sa osnovama Python-a, počećemo da se upoznajemo i sa praktičnim mogućnostima jezika kroz manje projekte i tutorijale, a tema prvog tutorijala koji je vezan za Python biće "implementacija specijalizovanog markup jezika za generisanje HTML sintakse": *

  • specifikaciju markup jezika osmislićemo sami (po ugledu na Markdown, verovatno najpopularniji markup jezik koji se koristi na brojnim sajtovima (i u drugim okolnostima)) **
  • za prevođenje sintakse koristićemo Python
  • usput ćemo se upoznati i sa konkretnim funkcijama za obradu teksta i rad sa listama u Python-u (koje su vrlo slične funkcijama iz JavaScript-a i PHP-a o kojima smo već pisali)

* Ako želite odmah da vidite "o čemu se radi", premotajte 'na brzaka' članak do prve dve slike (ali vratite se nazad). :)

** Verujemo da su mnogi čitaoci već upoznati sa Markdown sintaksom (videćemo primere u nastavku).

Šta je markup jezik?

Pre nego što 'pređemo na delo', red je da odgovorimo na osnovno pitanje iz naslova - šta su uopšte markup jezici?

Markup jezik je računarski jezik u kome se kombinacije znakova koriste kao "šifra" za označavanje delova koji ne predstavljaju običan tekst, već - imaju posebno (semantičko) značenje - što se nadalje može iskoristiti na različite načine.

Naravno, u svemu mora postojati i "nekakav" mehanizam za tumačenje markup jezika (prevodilac i sl), jer, ako otvorimo markup sintaksu u (običnom) editoru teksta - videćemo samo markup sintaksu u izvornom obliku. :)

Dobro poznati primer navedene prakse je jezik HTML, u kome tagovi - specifične kombinacije znakova - upućuju browser (koji se u navedenom kontekstu javlja kao "mehanizam za tumačenje markup sintakse"), na to da celine koje slede posle pojave otvarajućeg taga - imaju posebno značenje (naslov, paragraf, podebljan tekst i sl.) - što znači da takve celine treba interpretirati tj. prikazati, drugačije u odnosu na običan tekst.

Međutim ....

Iako HTML nedvosmisleno spada u markup jezike (ako je neko zaboravio - da se podsetimo: "veliko M" u skraćenici HTML doslovno je skraćenica za "Markup" :)), u današnje vreme, pojam markup jezika se odnosi pre svega na jezike za pojednostavljeno generisanje HTML sadržaja (a implementacija jednog takvog vrlo jednostavnog jezika, tj. prevodioca, glavna je tema članka).

Na ovom mestu, u potpunosti se usmeravamo na jezike koji su namenjeni pojednostavljenom kreiranju HTML-a, ali, deluje takođe da se susrećemo i sa pitanjem - "čemu sve to". :)

Odakle potreba za markup jezicima za pojednostavljeno kreiranje HTML-a?

Budući da je gotovo sigurno da će se mnogi čitaoci zapitati zašto bismo uopšte pojednostavljivali proces pisanja HTML-a (jer ne deluje da je u pitanju komplikovan 'proces'), ukratko ćemo objasniti odakle potreba za pristupom koji (u praktičnom smislu) predstavlja temu članka.

Dakle ....

Iako HTML jeste prilično jednostavan jezik, krajnje podesan za definisanje opšte strukture stranica, praksa pokazuje da unos (u većem obimu i "na duže staze"), tagova koji predstavljaju sadržaj stranice (naslovi, pasusi, liste i sl) - ipak izaziva zamor.

Ako se dvoumite - probajte sami: verujemo da vas neće 'mrzeti' da uokvirite nekoliko paragrafa <p> tagovima, međutim, ako treba uokviriti više desetina ili stotina tagova (pogotovo ako se takav posao obavlja svakog dana i sl) - to je već "sasvim druga priča". :)

Vraćamo se na 'tehnikalije' ....

Tehnička rešenja (osnovne ideje)

Zarad uspostavljanja 'istorijskog konteksta', navešćemo da su idejna i tehnička rešenje za generisanje HTML-a preko markup jezika postojala još pre nekoliko decenija, ali, može se reći da je prava popularnost takvih rešenja (ipak) postala izražena tek u poslednjih petnaestak godina.

U svakom slučaju, osnovna ideja je da sintaksa markup jezika što više liči na "baš običan tekst", ali (naravno) - da izlaz na kraju bude HTML kod:

  • pasusi se pišu bez ikakvih dodatnih oznaka
  • ostali tekstualni elementi se zapisuju na pojednostavljen način
  • na kraju, markup tekst se prevodi u HTML

Već smo pomenuli da, u idejnom smislu, jezik Markdown * predstavlja uzor za jednostavni markup jezik koji kreiramo, budući je u pitanju jezik koji se koristi za uređivanje članaka na brojnim sajtovima (rekli bismo da je "najočigledniji primer", u navedenom kontekstu, sajt GitHub).

Jezik koji ćemo implementirati u članku - biće jednostavniji i od jezika Markdown, od koga ćemo direktno pozajmiti samo način zapisivanja naslova (za Markdown smo se odlučili upravo zato što je u pitanju sintaksa koja je široko rasprostranjena i (nadamo se), bliska mnogim čitaocima).

* Da .... Markdown je zapravo markup jezik. 🙂

Takođe, napomenimo i to da markup jezici (čija je svrha generisanje HTML-a), tipično nisu podesni za kreiranje celokupne strukture stranice, ali, naravno da postoje i 'druge opcije'.

Pre svega, za definisanje osnovne strukture stranice, i dalje je više nego primereno koristiti standardni HTML (što u praktičnom smislu ipak zavisi od obima kodnog teksta), a postoje takođe i endžini za rad sa šablonima kao što su EJS, Pug, Handlebars, Jinja i sl, međutim, iako su (takođe) u pitanju programi za automatsko generisanje HTML-a, radi se ipak o drugačijem pristup u odnosu na markup jezike, i stoga će navedeni programi za rad sa šablonima biti tema za diskusiju nekom drugom prilikom.

Specifikacija DIY markup jezika

Markup jezik iz "domaće radinosti" koji opisujemo u članku (zvaćemo ga "Idiosinkratični Markdown"), odlikuje se vrlo jednostavnom sintaksom i biće prevođen na sledeći način:

  • tekst koji nije posebno označen, biće pretvoren u paragrafe *
  • naslovi h1-h6 označavaju se kombinacijama uzastopnih "taraba", od # do #######(očigledna pozajmica iz Markdown specifikacije, zarad što lakšeg navikavanja za čitaoce koji već koriste GitHub i slične sajtove)
  • HTML blokovi (koji će biti direktno kopirani na izlaz), počinju kombinacijom znakova `` (u zasebnom redu), i završavaju se kombinacijom znakova ~~ (takođe u zasebnom redu)
  • liste počinju znakom * (u zasebnom redu), a završavaju se kombinacijom ** (takođe u zasebnom redu)
  • redovi u kojima su zapisani pojedinačni elementi lista, počinju znakom \t ("TAB")
  • linijski (tj. "inline") HTML elementi, mogu se pisati direktno unutar pasusa i elemenata liste **

* Da budemo 'skroz precizni': svaka podniska koja je zapisana između početka reda i znaka za prelazak u novi red - i pri tom nije posebno označena (tj. zapisana kao naslov/podnaslov, element liste i sl), biće uokvirena <p> tagovima.

** Za pojednostavljivanje unosa pomenutih HTML tagova koji će biti direktno preslikani u izlaznu datoteku, mogu se koristiti tzv. 'snipeti' (šabloni za brzinski unos teksta, koji se lako mogu definisati i pozivati u ogromnoj većini editora).

Pogledajmo primer prevođenja sintakse.

Sledeći markup kod ....

		
# Naslov

Prvi pasus (vrlo kratak).

## Podnaslov

Prikazujemo primer prevođenja za:

*
	pasuse
	naslove
	liste
**
		
	
Slika 1. - Primer markup sintakse za koju implementiramo prevodilac u Python-u.

.... prevodi se u odgovarajući HTML kod:

		
<h1>Naslov</h1>

<p>
	Prvi pasus.
</p>

<h2>Podnaslov</h2>

<p>
	Prikazujemo primer prevođenja za:
</p>

<ul>
	<li>pasuse</li>
	<li>naslove</li>
	<li>liste</li>
</ul>
		
	
Slika 2. - HTML kod koji nastaje posle prevođenja markup sintakse sa prethodne slike.

Implementacija u Python-u

Uz iole pažljiviju analizu specifikacije iz prethodnog poglavlja, neće biti teško da ustanovimo i osnovne smernice za realizaciju prevodioca za jednostavni markup jezik (kojim se bavimo):

  • svi tokeni (tj. celine u tekstu koje imaju posebno značenje), zapravo su pojedinačni redovi početnog teksta *
  • od važnijih struktura podataka, potrebno je koristiti dve liste:
    • listu za čuvanje pojedinačnih redova teksta (lista nastaje deljenjem ulaznog teksta)
    • listu tokena (koja nastaje obradom liste pojedinačnih redova)
  • ceo proces obavićemo u tri ** prolaska kroz tekst:
    • rastavljanje početnog teksta na redove
    • analiza sadržaja i kreiranje tokena
    • 'sastavljanje' HTML-a

U nastavku (ispod napomene), sledi opis pojedinačnih funkcija.

* Prva stavka u listi, takođe se odnosi na pasuse, u tom smislu da je svaki pasus u ulaznom tekstu (odnosno, budući paragraf u HTML-u), zapravo zapisan u jednom redu.

U mnogim programima za obradu teksta, unošenje znaka za prelazak u novi red predstavlja način za kreiranje novog pasusa (ne samo novog reda) - što važi i za jednostavni prevodilac koji pravimo (kao što smo još na početku nagovestili).

Shodno navedenom, potrebno da ceo sadržaj pasusa bude (upravo) - u jednom redu.

** Ceo proces može se inače obaviti u najviše dva prolaska, ali, prikazaćemo postupak koji je lakši za razumevanje i pregledniji (i tek neznatno sporiji u praksi).

Naravno, u nekom kasnijem trenutku, možete prepraviti algoritam po sopstvenoj želji.

Učitavanje teksta

Tekst koji će biti prosleđen funkciji za obradu, učitava se iz datoteke (koristićemo datoteku sa nazivom ulaz.txt, za koju ćemo podrazumevati da se nalazi u istom direktorijumu kao Python skripta koju pišemo).

U članku o obradi tekstualnih datoteka u C-u i Python-u podrazumevalo se da programi operišu sa ASCII datotekama, a ako bi to bio slučaj i sa datotekama koje učitavamo zarad prevođenja markup sintakse, Python funkcija za čitanje datoteke mogla bi se implementirati na vrlo jednostavan način:

		
def ucitavanje():
	f = open("./ulaz.txt", "r")
	s = f.read()
	f.close()
	return s
		
	
Slika 3. - Funkcija za učitavanje teksta iz datoteke.

Da se podsetimo: preko atributa "r" ("read"), u funkciji open, navedeno je da se datoteka otvara u režimu čitanja.

Međutim, situacija je ponešto komplikovanija ukoliko je potrebno učitavati članke na srpskom jeziku (ili bilo kom drugom jeziku koji koristi Unicode znakove 'kojih nema u ASCII tabeli'), i stoga je potrebno prepraviti gornju funkciju.

Pri učitavanju datoteke koja se kodira po standardu UTF-8, potrebno je obaviti i dekodiranje. *

		
def ucitavanje():
	f = open("./ulaz.txt", "rb")
	s = f.read().decode("utf-8")
	f.close()
	return s
		
	
Slika 4. - Funkcija za učitavanje teksta iz datoteke - prepravljena tako da dozvoljava učitavanje UTF-8 datoteka.

Argument "rb" (read binary), u funkciji open, ovoga puta sugeriše da je u pitanju otvaranje binarne datoteke.

.... iako - u praktičnom smislu - nije u pitanju čitanje binarne datoteke. :)

(Međutim, znamo ipak i to da UTF-8 znakovi nisu definisani ni kao nizovi bajtova u kojima svaki bajt predstavlja jedan znak.)

* Kasnije, kada na red dođe upis u HTML datoteku, biće potrebno obaviti enkodiranje sadržaja datoteke.

Podela teksta na redove i kreiranje tokena

Pošto je tekst učitan iz datoteke, ulaznu nisku je prvo potrebno podeliti na redove, a potom i na tokene (prvo ćemo sagledati kod u celini):

		
def idiosync_parse():
	s      = ucitavanje
	redovi = ucitavanje.split('\n') 
	tokeni = []
	s      = ""
	parse  = True

	for r in redovi:
		if r == "\r" or r == "":
			continue
		
		if parse == True:
			if r.startswith("``"):
				parse = False
				continue
			ucitavanje_tokena(r, tokeni)			
		else:
			if r.startswith("~~"):
				parse = True
				tokeni.append(["", "", ""])
				continue
			tokeni.append([r, "", ""])

	for t in tokeni:
		s = s + t[0] + t[1] + t[2] + "\n"

return s
		
	
Slika 5. - Funkcija za pretvaranje ulaznog markup teksta u HTML, prikazana ovde u celosti (radi preglednosti). Pojedinačnim delovima ćemo posvetiti više pažnje naknadno.

Osvrnimo se (nešto detaljnije), na bitne delove glavne funkcije idiosync_parse.

Funkcija split deli početnu nisku na određeni broj podniski (koje se smeštaju u listu redovi), a "rezovi" se dešavaju svaki put kada program naiđe na znak za prelazak u novi red.

		
s      = ucitavanje
redovi = ucitavanje.split('\n') 
tokeni = []
s      = ""
parse  = True
		
	
Slika 6. - Analiza funkcije za pretvaranje ulaznog teksta u HTML - deo funkcije koji se tiče učitavanja teksta i podele teksta na redove.

Sada više ne radimo sa ulaznim tekstom direktno, već, sa listom niski:

		
[
	['# Naslov'],
	[''],
	['Prvi pasus (vrlo kratak)'],
	[''],
	['## Podnaslov'],
	[''],
	['Na ovom jednostavnom primeru, videli smo:'],
	[''],
	['*'],
	['\tpasuse'],
	['\tnaslove'],
	['\tliste'],
	['**']
]
		
	
Slika 7. - Prikaz sadržaja liste "redovi" koja nastaje preko funkcije split.

U nastavku, glavna for petlja prolazi kroz sve "redove" * (od prvog do poslednjeg).

* Posle podele, u pitanju su nezavisni elementi niza niski, ali, u idejnom smislu, i dalje su u pitanju redovi ulaznog teksta (i zato ćemo za pojedinačni element liste povremeno koristiti naziv "red").

Prvi uslov proverava da li je red prazan ili sadrži (samo) znak '\r' * i - ako je navedeni uslov zadovoljen - prelazi se na sledeći red za ispitivanje.

		
for r in redovi:
	if r == "\r" or r == "":
		continue
		
	
Slika 8. - Analiza funkcije za pretvaranje ulaznog teksta u HTML - osnovni okvir for petlje.

* Pojava znaka '\r' je specifičnost (tekstualnih) datoteka koje su formatirane na operativnom sistemu Windows, ali - budući da je u pitanju OS koji koristi većina čitalaca (kao i ostalih korisnika računara, inače) - ostavićemo delove koda koji se tiču provere pojave znaka '\r'.

U drugom uslovu, boolean promenljiva parse određuje da li će red (koji je u obradi), biti tretiran kao deo koji je potrebno pretvoriti u HTML (naslov, paragraf ili lista) - ili će biti prenet direktno - i stoga promenljiva menja vrednost shodno pojavi kombinacije znakova ``, odnosno ~~.

		
if parse == True:
	if r.startswith("``"):
		parse = False
		continue
	ucitavanje_tokena(r, tokeni)			
else:
	if r.startswith("~~"):
		parse = True
		tokeni.append(["", "", ""])
		continue
	tokeni.append([r, "", ""])
		
	
Slika 9. - Analiza funkcije za pretvaranje ulaznog teksta u HTML - uslov koji određuje režim kreiranja tokena (opcije: 1. učitavanje preko metode koja kreira HTML tagove oko sadržaja reda; 2. prosto kopiranje reda).

Učitavanje tokena obavlja se preko zasebne metode.

Metoda za učitavanje tokena

Deo koda za razvrstavanje tokena (pri učitavanju), krajnje je jednostavan, međutim - s obzirom na veći broj grananja - navedeni deo koda radije ćemo izdvojiti u zasebnu funkciju:

		
def ucitavanje_tokena(r, tokeni):
	if r.startswith("!!"):
		return	
	if r.startswith("####"):
		tokeni.append(["<h4>", r.lstrip('#').strip(), "</h4>\n"])
		return
	if r.startswith("###"):
		tokeni.append(["<h3>", r.lstrip('#').strip(), "</h3>\n"])
		return
	if r.startswith("##"):
		tokeni.append(["<h2>", r.lstrip('#').strip(), "</h2>\n"])
		return
	if r.startswith("#"):
		tokeni.append(["<h1>", r.lstrip('#').strip(), "</h1>\n"])
		return
	if r.startswith("**"):
		tokeni.append(["</ul>\n", "", ""])
		return
	if r.startswith("*"):
		tokeni.append(["<ul>", "", ""])
		return
	if r.startswith("\t"):
		tokeni.append(["\t<li>", r.strip(), "</li>"])
		return

	# Ako red nije ništa 'posebno', onda je <p> :)
	tokeni.append(["<p>\n\t", r.strip(), "\n</p>\n"])
		
	
Slika 10. - Analiza funkcije za kreiranje tokena shodno sadržaju ulaznog reda (u praktičnom smislu - "switch grananje", zapisano na malo drugačiji način).

Do metode ucitavanje_tokena ne dolaze prazni tokeni, i stoga samo treba proveriti da li red predstavlja:

  • komentar - !! tekst_komentara
  • naslov - #{1, 6} tekst_naslova
  • otvaranje liste - *
  • zatvaranje liste - **
  • unutrašnji element liste - na početku reda nalazi se znak \t

Ako nijedna od navedenih provera ne vrati vrednost True, može se smatrati da red (koji je trenutno u obradi), predstavlja običan pasus.

Po završetku, lista redova postaje lista tokena:

		
['<h1>', 'Naslov', '</h1>']
['<p>', 'Prvi pasus (vrlo kratak).', '</p>']
['<h2>', 'Podnaslov', '</h2>']
['<p>', 'Na ovom jednostavnom primeru, videli smo:', '</p>']
['<ul>', '', '']
['<li>', 'pasuse', '</li>']
['<li>', 'naslove', '</li>']
['<li>', 'liste', '</li>']
['</ul>', '', '']
		
	
Slika 11. - Prikaz sadržaja liste "redovi" koja nastaje preko funkcije split.

Svaki token sada je prigodno formatiran u vidu liste sa tri elementa, koji predstavljaju: otvarajući tag, sadržaj i zatvarajući tag.

Po povratku u metodu idiosync_parse, lako se može formirati izlazna vrednost (spajanjem elemenata liste u nisku):

		
for t in tokeni:
	s = s + t[0] + t[1] + t[2] + "\n"
		
	
Slika 12. - Povratak na analizu funkcije za pretvaranje ulaznog teksta u HTML - poslednji deo - kreiranje izlaznog teksta prolaskom kroz listu tokena.

Posle svega, ostaje samo još da napišemo metodu za upis novog sadržaja u datoteku, i da pozovemo program.

Metoda za zapisivanje datoteke

Metodu za upis u datoteku, takođe ćemo definisati na elegantan ("pajtonovski") način:

		
def zapisivanje():
	s = s.encode("utf-8")
	f = open("./izlaz.html", "wb")
	f.write(s)
	f.close()
	return
		
	
Slika 13. - Metoda za zapisivanje izlaznog HTML koda u datoteku (i ovoga puta poštujemo UTF-8 specifikaciju, i stoga ćemo sadržaj niske koja nastaje prevođenjem, prvo enkodirati, a zatim upisati u datoteku).

Da se podsetimo: prvo je potrebno enkodirati tekst, po standardu UTF-8 (i zato se u funkciji open koristi atribut wb ("write binary")).

Pokretanje programa

Pošto su sve metode spremne, glavni deo programa neće biti komplikovan ....

		
ulaz  = ucitavanje()
izlaz = idiosync_parse(ulaz)
zapisivanje(izlaz)
		
	
Slika 14. - Glavni kod za pokretanje procedure za pretvaranje teksta u HTML.

.... i sada - pod uslovom da smo spremili datoteku ulaz.txt - lako možemo pokrenuti skriptu.

Ideje za vaše buduće projekte ....

Skripta koju smo opisali u članku nastala je iz potrebe (mnogo je lakše pisati članke preko jednostavnog markup jezika nego pisati čist HTML :)), a u skorijoj budućnosti napisaćemo i nekoliko članaka koji se tiču nešto detaljnije obrade tokena (sa kakvom se npr. srećemo u syntax highlighter-ima, lekserima, parserima i sl).

U praktičnom smislu, "pozadinska ideja" u svim navedenim primerima je sledeća: kreiranje jednostavnih DIY alata koji olakšavaju svakodnevne poslove u obradi teksta.

I upravo to može biti ideja i za vaše samostalne projekte.

Recimo, možete osmisliti skriptu koja izabrani deo teksta uokviruje tagovima sa specifičnom kombinacijom atributa koju često koristite, ili (možda), skriptu koja formatira CSS na određeni način (vrlo smo skloni da sa vama u budućnosti podelimo i jednu takvu skriptu koju smo takođe napisali za sopstvene potrebe).

Ili, ako želite da se pozabavite "unapređenom verzijom prethodne ideje", možete probati da kreirate plugin-ove za editore (doduše, u pitanju je nezanemarljivo kompleksniji i teži 'zahvat').

Editori teksta o kojima smo pisali u članku o obradi teksta takođe pružaju mogućnost proširivanja osnovnih mogućnosti preko plugin-ova, pri čemu plugin-ovi nisu ništa drugo nego skripte pisane u JavaScript-u (Atom, VSC), ili Python-u (Sublime).

Prosto rečeno, ako imate želju da se bavite pisanjem plugin-ova za editore, zadovoljeni su svi tehnički preduslovi. :)

Doduše .... pisanje plugin-ova za editore podrazumeva da je potrebno savladati i odgovarajući API (Application Programming Interface - softver koji omogućava dodavanje funkcionalnosti u osnovni program, preko programskog koda), a takvo upoznavanje, u zavisnosti od vašeg trenutnog nivoa veštine, količine slobodnog vremena i dobre volje, može predstavljati: ili sjajan izazov, ili nepotrebno opterećenje.

Ako je u pitanju "opcija #2" (to jest, ne želite baš odmah da se bacite na pisanje plugin-ova za tekstualne editore), probajte prvo, za vežbu, da napravite web formular koji preko JavaScript-a obavlja prevođenje, ili desktop program u nekom drugom jeziku (C/C++, C#, Java i sl).

Polako, tokom vremena, steći ćete poverenje u sopstvene sposobnosti, i ono što u prvom trenutku deluje teško, postaće sasvim dostižno (kao što navodi slogan jedne poznate firme za izradu muzičkih instrumenata - "kada budete spremni"). :)

Autor članka Nikola Vukićević Za web portal codeblog.rs
Napomena: Tekstovi, slike, web aplikacije i svi ostali sadržaji na sajtu codeblog.rs (osim u slučajevima gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog pismenog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook LinkedIn Twitter Viber WhatsApp E-mail
početna > Članci > Tutorijal - Implementacija markup jezika u Python-u
codeBlog codeBlog
Sajt posvećen popularizaciji kulture i veštine programiranja.
Napomena: Tekstovi i slike na sajtu codeblog.rs (osim u slučajevima, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook - logo
Instagram - logo
LinkedIn - logo
Twitter - logo
E-mail
Naslovna
   •
Uslovi korišćenja
   •
Obaveštenja
   •
FAQ
   •
Kontakt