Ubuntu Suomen keskustelualueet
Ubuntun käyttö => Ohjelmointi, palvelimet ja muu edistyneempi käyttö => Aiheen aloitti: teele - 12.01.21 - klo:15.26
-
Kokeilussa olisi seuraavanlainen syöterivi
nimi aika kilomäärä laji täsmennys
Tietotyypit olisi suunniteltu tällaisiksi pythonissa
string int float string string
Ja sitten haluttaisiin tarkistaa, että syötettävät arvot ovat ensiksi kieliopillisesti oikein ja sitten, jos kaikki onnistuu, että annetut arvot ovat muuten sopivia.
Yksi tapa olisi varmaan pythonin regex, mutta tällainen kysymys on varmaan ratkaistu jo aikaisemmin monta kertaa muillakin tavoilla eikä nyt edes ole ideana alkää kirjoittaa kaikkea ihan reqexistä lähtien.
Löytyisikö asiaan valmisratkaisuja. Netistä en ole ainakaan vielä näppärää ratkaisua löytänyt.
-
Tuo ei oikein riitä luonnehdinnaksi: pitäisi tietää, mitä merkkejä kukin merkkijono voi sisältää ja miten kentät erotetaan.
Mutta olettaen että erottimena on sanaväli eikä sitä käytetä merkkijonoissa, tuossa pärjäisi str.split()-metodilla. Esimerkiksi jos ”rivi” sisältää rivillisen tekstiä:
nimi, aika, kilot, laji, tasmennys = rivi.strip().split()
try:
aika = int(aika)
kilot = float(kilot)
except ValueError:
print("Virhe! Virhe!")
Tuossa antaisit siis Pythonin itsensä tarkistaa lukujen oikeellisuuden yrittämällä vain ronskisti muuttaa merkkijonon luvuksi.
Jos syötteessä liukuluvussa desimaalierottimena on pisteen sijaan pilkku, täytyy aavistuksen viilata:
kilot = float(kilot.replace(',', '.'))
Jos muuten syöte on CSV-tiedosto, kannattaa saman tien käyttää csv-moduulia. Sen avulla lainausmerkkien ym. hallinta sujuu helpoimmin.
-
Juuri noilla neuvoilla käpistely etenee
rivi1 = 'päre 20210112 10.123 tarvike kattopäreet'
rivi2 = rivi1.strip().split()
print(rivi2)
try:
aika = int(rivi2[1])
kilot = float(rivi2[2])
except ValueError:
print("Virhe! Virhe!")
print(kilot)
print(aika)
En vielä laita säiettä ratkaistuksi, tässä voi tulla vielä muita ongelmia .... :)
-
Puhutun kielen kieliopillisesti vähän haastavampi toteuttaa tietokonekielellä, joskaan ei mahdoton. Tarkoitit varmaan Python-kielellä kieliopillisesti oikein?
Tässä aamu vierähtänyt humaanimman keskustelun tiimellyksessä Uusi Suomi -blogissa, niin tuli yhdistettyä tuo kielioppi nimenomaan luonnolliseen kieleen ensiksi, anteeksi tämä huomautus.
Mitä Pythonin versiota käytät? Tuli mieleen tuosta ajasta, että mitä UNIX-järjestelmässäkin lasketaan aika long-tyyppisestä muuttujasta, mutta Python 3 -versiossa ei ole enää longia. Kakkosessa on vielä. Mutta aikaan ehkä kannattaisi hyödyntää datetime -moduulia kokonaisluvun sijaan, jos ajatellaan jotain tietokantaan vientiä arkistointia varten tiedon varastointia yms.
Yksinkertainen esimerkki, miten Pythonissa saa käyttöön ajan ohjelmoinnin.
import datetime
x = datetime.datetime.now()
print(x)
-
Mitä Pythonin versiota käytät? Tuli mieleen tuosta ajasta, että mitä UNIX-järjestelmässäkin lasketaan aika long-tyyppisestä muuttujasta, mutta Python 3 -versiossa ei ole enää longia.
Kenenkään ei pitäisi enää käyttää Python 2:ta, koska sen tuki on päättynyt.
-
Mitä Pythonin versiota käytät? Tuli mieleen tuosta ajasta, että mitä UNIX-järjestelmässäkin lasketaan aika long-tyyppisestä muuttujasta, mutta Python 3 -versiossa ei ole enää longia.
Kenenkään ei pitäisi enää käyttää Python 2:ta, koska sen tuki on päättynyt.
Eipä sitä taida enää ketään käyttääkään, ainakaan uusien sovellusten ohjelmointiin. En tiedä sitten, onko teollisuuskäytössä jotkin ohjelmistokehittäjät ylläpitävät vielä Python 3:a edeltävillä versioilla ohjelmoituja ohjelmakoodeja. Voi olla, että ei.
Itsellänikin on Ubuntu-kannettavassani Python 2 asennettuna, mutta ohjelmoin Python 3:lla sen, mitä ohjelmoin Pythonilla sillä ajan kuluksi välillä kun katson jotain uutta.
-
Tuosta tuli vielä mieleen, kun mainitsin, että UNIX-järjestelmässä lasketaan long-tyyppisestä muuttujasta aika, mutta asia ei ole niin yksinkertainen näköjään.
https://en.wikipedia.org/wiki/Unix_time (https://en.wikipedia.org/wiki/Unix_time)
UNIX-aika ja myös siis Linux ja Ubuntussa ajan määrittely, Date -ohjelma ikäänkuin.
Aloin kaivamaan netistä ohjeita, miten tuosta long-tyyppisestä millisekuntti-arvosta saa laskettua, laskukaavaa tai jotain, jonkin ihmisen ymmärrettävään muotoon muinaisten sumerilaisten 12/60 -järjestelmään perustuvan ajankohdan. Voisi olla ihan mielekäs harjoitus ohjelmoinnin osalta, jos tulee tilanne, että ei millään keksi mitään tekemistä paremman puutteessa.
Ei tosin tuosta ajanlaskenta -metodista mitään käytännön hyötyä ole, kun tosiaan yksi sekunti on 1000 millisekuntia, niin siitähän ajan saa, ja kaikissa ohjelmointikielissä varmaan jo implementoitu valmiina jokin päivämäärä/aika -funktio tai -kirjasto, että ei tarvitse lähteä tyhjästä laskemaan.
Mutta kai aikaa voi käyttää pyörän keksimiseen norsunluutasolla, kuka käyttää aikansa mihinkin paremman tekemisen puutteessa?
Unixin ajan alkuarvo 01.01.1970 on nolla, ja siitä sitten varmaan millisekunteina laskettuna siitä eteenpäin loput ajat, ketä tuon Date, tai nuo muut aika-sovellukset ohjelmoinut alunperin Unixin kehittäjät.
-
Java-koodia
import java.util.Date;
public class Aika {
public static void main(String[] args) {
//Muuttujien määrittely
long aika = System.currentTimeMillis();
long muutettu = aika/1000/60/24;
long tulevaisuus = Long.MAX_VALUE;
//Luodaan Date -tietotyypin ilmentymät, oliot.
Date now = new Date(aika);
Date history = new Date(muutettu);
Date endOfWorld = new Date(tulevaisuus);
//Tulostetaan testitulokset
System.out.println(now);
System.out.println(history);
System.out.println(endOfWorld);
//Onkohan ihminen vielä 292278994 -vuonna elossa ja UNIX-pyörimässä palvelimella?
}
}
Tuloste:
Thu Jan 14 15:35:47 EET 2021
Thu Jan 01 02:18:38 EET 1970
Sun Aug 17 09:12:55 EET 292278994
Tuon jakolaskun tuossa koodissa pitäisi antaa aika lähelle 1970-vuoden tammikuun alkua olevan ajan, eli lähelle nollaan jaon tuloksena antaa tuon long -muuttujan, mutta tuossa varmaan vähän reilun kahden tunnin heitto kun Suomessa on Greenwich MeanTime +2 -aikavyöhyke käytössä, niin jos tuon 2 tuntia vähentää, niin aika lähelle 00:00:00 pääseen 01.01.1970.
Tuolla Unix-Time -Wikipedian artikkelista selvisi, kun luin sen nyt ensimmäistä kertaa, että UNIX-ajanlaskun perustuu UTC -alkukantaiseen aikavyöhykestandardiin, joka on vaikutteena toiminut tuolle GMT:lle.
Suora lainaus Wikipediasta:
"Coordinated Universal Time It is effectively a successor to Greenwich Mean Time (GMT).".
Tuolla artikkelissa mainitaan myös, että UNIXin ajan alkuarvo ei ole aito esitys tuosta UTC-standardin nolla-arvosta.
-
Tuosta tuli vielä mieleen, kun mainitsin, että UNIX-järjestelmässä lasketaan long-tyyppisestä muuttujasta aika, mutta asia ei ole niin yksinkertainen näköjään.
https://en.wikipedia.org/wiki/Unix_time (https://en.wikipedia.org/wiki/Unix_time)
UNIX-aika ja myös siis Linux ja Ubuntussa ajan määrittely, Date -ohjelma ikäänkuin.
Aloin kaivamaan netistä ohjeita, miten tuosta long-tyyppisestä millisekuntti-arvosta saa laskettua, laskukaavaa tai jotain, jonkin ihmisen ymmärrettävään muotoon muinaisten sumerilaisten 12/60 -järjestelmään perustuvan ajankohdan. Voisi olla ihan mielekäs harjoitus ohjelmoinnin osalta, jos tulee tilanne, että ei millään keksi mitään tekemistä paremman puutteessa.
Ei tosin tuosta ajanlaskenta -metodista mitään käytännön hyötyä ole, kun tosiaan yksi sekunti on 1000 millisekuntia, niin siitähän ajan saa, ja kaikissa ohjelmointikielissä varmaan jo implementoitu valmiina jokin päivämäärä/aika -funktio tai -kirjasto, että ei tarvitse lähteä tyhjästä laskemaan.
Mutta kai aikaa voi käyttää pyörän keksimiseen norsunluutasolla, kuka käyttää aikansa mihinkin paremman tekemisen puutteessa?
Unixin ajan alkuarvo 01.01.1970 on nolla, ja siitä sitten varmaan millisekunteina laskettuna siitä eteenpäin loput ajat, ketä tuon Date, tai nuo muut aika-sovellukset ohjelmoinut alunperin Unixin kehittäjät.
Heeh tuli toissa syksynä painittua pythonin, unix-ajan ja perinteisen ajan kanssa. En nyt muista heti kättelyssä minkä ratkaisun kehitin, sen muistan että erillistä valmista moduulia käyteltiin ja sai sitten merkkijonojakin manipuloida urakalla :D
-
Heeh tuli toissa syksynä painittua pythonin, unix-ajan ja perinteisen ajan kanssa. En nyt muista heti kättelyssä minkä ratkaisun kehitin, sen muistan että erillistä valmista moduulia käyteltiin ja sai sitten merkkijonojakin manipuloida urakalla :D
Varmaan mielenkiintoinen harjoitus. Jos löydät jostain harjoitusesitys-ratkaisusi, niin pistä vaikka jakoon, ihan mielenkiintoista olisi nähdä, miten ajan kanssa toimisit.
-
Se olikin ihan simppeli, datetime moduuli muuntaa näppärästi: datetime.fromtimestamp(epoch)
Yksi toinen harjoituksista tuotti enemmän päänvaivaa datamuunnosten kanssa...tämmöinen simppeli auringonlaskuaikoja tuottava ohjelma (ei toimi ilman api-keytä joka tuosta on obfuskoitu):
import urllib.parse
import requests
import json
from datetime import datetime
import time as time_
from datetime import time, datetime
from timezonefinder import TimezoneFinder
from pytz import timezone, utc
tf=TimezoneFinder()
#riippuvuudet, muista asentaa timezonefinder ja pytz
main_api ="http://www.mapquestapi.com/geocoding/v1/address?"
key = "******"
loc1 = "&location="
print("Anna kaupunki")
locationcity = input()
print("Anna maa englanniksi")
locationcnt = input()
locationcountry = "+"+locationcnt
print("Anna postinumero")
locationzp = input()
locationzip = "+"+locationzp
location =loc1+locationcity+locationcountry+locationzip #täälysti käsittely palvelupyynnön tuolle geolokaatioapi:lle
url = main_api+key+location
json_data = requests.get(url)
data=json_data.json()
lat2 = data['results'][0]['locations'] #hirmuinen datanpyöritys
lat3 = []
for item in lat2: #tääpalauttaa sitten vihdoin sen latitudin ja longitudin
lat3.append(item['latLng'])
latfinal =[]
for item in lat3: #nyt saataan latitudi listana
latfinal.append(item['lat'])
lngfinal=[] #ja longitudi
for item in lat3:
lngfinal.append(item['lng'])
lat = latfinal[0] #syötetään ne
lng = lngfinal[0]
main_api2="https://api.sunrise-sunset.org/json?"
formatted = "1"
url2 = main_api2 +urllib.parse.urlencode({"lat":lat, "lng":lng, "formatted":formatted})
json_data = requests.get(url2).json()
#otetaan nousuaika datasta, muokataan sieltä tunti int:iksi ja muutetaan 24h muotoon
nousuaika = json_data['results']['sunrise']
if len(nousuaika) == 10:
nousuaika = "0"+nousuaika
nousuaikah=nousuaika[:2]
nousuaikam=nousuaika[3:5]
nousuaikas=nousuaika[6:8]
nousuinth = int(nousuaikah)
noipap= nousuaika[9:]
if noipap == "PM":
nousuinth = 12+nousuinth
laskuaika = json_data['results']['sunset']
#sama laskuajalle
if len(laskuaika) == 10:
laskuaika = "0"+laskuaika
laskuaikah=laskuaika[:2]
laskuaikam=laskuaika[3:5]
laskuaikas=laskuaika[6:8]
laskuinth = int(laskuaikah)
laipap= laskuaika[9:]
if laipap == "PM":
laskuinth = 12+laskuinth
#täällä tapahtuu aikavyöhyketaikuus
today = datetime.now()
tz_target = timezone(tf.certain_timezone_at(lng=lng, lat=lat))
today_target = tz_target.localize(today)
today_utc = utc.localize(today)
offset = (today_utc - today_target).total_seconds() / 3600
nousuaikaloc = int(nousuinth +offset)
nousuaikalocs = str(nousuaikaloc)
print("Aurinko nousee "+nousuaikalocs+":"+nousuaikam+":"+nousuaikas)
laskuaikaloc = int(laskuinth + offset)
laskuaikalocs = str(laskuaikaloc)
print("Aurinko laskee "+laskuaikalocs+":"+laskuaikam+":"+laskuaikas)
Paljon muuttujia mutta no ne oli tarpeen jotta itse pysyi kärryillä ohjelman puuhista...
-
location =loc1+locationcity+locationcountry+locationzip #täälysti käsittely palvelupyynnön tuolle geolokaatioapi:lle
url = main_api+key+location
json_data = requests.get(url)
data=json_data.json()
lat2 = data['results'][0]['locations'] #hirmuinen datanpyöritys
lat3 = []
for item in lat2: #tääpalauttaa sitten vihdoin sen latitudin ja longitudin
lat3.append(item['latLng'])
latfinal =[]
for item in lat3: #nyt saataan latitudi listana
latfinal.append(item['lat'])
lngfinal=[] #ja longitudi
for item in lat3:
lngfinal.append(item['lng'])
lat = latfinal[0] #syötetään ne
lng = lngfinal[0]
Osoitteen muuntaminen koordinaateiksi onnistuisi helposti GeoPy-kirjaston avulla: https://geopy.readthedocs.io/en/stable/#
#otetaan nousuaika datasta, muokataan sieltä tunti int:iksi ja muutetaan 24h muotoon
nousuaika = json_data['results']['sunrise']
if len(nousuaika) == 10:
nousuaika = "0"+nousuaika
nousuaikah=nousuaika[:2]
nousuaikam=nousuaika[3:5]
nousuaikas=nousuaika[6:8]
nousuinth = int(nousuaikah)
noipap= nousuaika[9:]
if noipap == "PM":
nousuinth = 12+nousuinth
laskuaika = json_data['results']['sunset']
#sama laskuajalle
if len(laskuaika) == 10:
laskuaika = "0"+laskuaika
laskuaikah=laskuaika[:2]
laskuaikam=laskuaika[3:5]
laskuaikas=laskuaika[6:8]
laskuinth = int(laskuaikah)
laipap= laskuaika[9:]
if laipap == "PM":
laskuinth = 12+laskuinth
dateutil.parser auttaa kellonaikojen ja päivämäärien lukemisessa. Ei kuulu Pythonin standardikirjastoon, mutta on hyvin yleisesti käytetty paketti.
Asennus yhdelle käyttäjälle:
pip3 install --user python-dateutil
Tai koko järjestelmän käyttöön:
sudo pip3 install python-dateutil
Esimerkki:
import dateutil.parser
d = dateutil.parser.parse("5:12:59 PM") # Palauttaa datetime-objektin
print(d)
2021-01-15 17:12:59
-
Noi oli yhden ohjelmointikurssin harjoitustöitä, joissa pääpaino oli ulkoisten api:den käytössä, siksi niitä tuossa koodissa vilisee. Suurin osa hommaa vaan oli muuntaa jonkin api:n tuloste toisen hyväksymään muotoon.
-
Meni tuo obfusikointikeskustelu sen verran pahasti ohi aiheen, että päädyin jakamaan kahtia. Voitte keskustella siitä tuolla:
https://forum.ubuntu-fi.org/index.php?topic=56045.0
-
Yksi kohta siinä syöterivin tarkistuksessa on päivämäärän tarkistus. Päivämäärä haluttaisiin antaa muodossa vvvvkkpp niin, että tarpeelliset nollat lisätään merkkijonoon ja halutaan, että merkkijonon pituus on aina 8 ja vain tietysti oikeat, todelliset päivämäärät hyväksytään.
Tällaista on kokeiltu
from dateutil.parser import parse
import re
def is_valid_date(d):
if d and len( re.sub(r'^\s+|\s+$', '', d) ) == 8:
try:
parse(d)
return True
except:
return False
return False
x = input('anna x: ')
while x != 'x':
print(is_valid_date(x))
x = input('anna x: ')
Se, onko koodissa joitain porsaanreikiä, joista pääsee syöttämään vääränlaisia päivämääriä, ei ole vielä ihan selvillä. Itsehän omia virheitä ei löydä :)
-
”Halutaan antaa” vai ”oletetaan annetuksi”? Jälkimmäinen on helpompi, esimerkiksi:
import re
valid_date = re.compile(r'^(\d\d\d\d)(\d\d)(\d\d)$')
Näin tarkistus kävisi yksinkertaisesti tyyliin valid_date.match('20210121') → palauttaa None ellei ole kelvollinen, muulloin Match-olion.
Sulutuksen ansiosta osat on helppo lukea vuosi-, kuukausi- ja päivämuuttujiksi suoraan .groups()-metodilla:
try:
year, month, day = valid_date.match(annettu_jono).groups()
except AttributeError:
print('Virheellinen jono')
-
def is_valid_date(d):
if d and len( re.sub(r'^\s+|\s+$', '', d) ) == 8:
try:
parse(d)
return True
except:
return False
return False
Tuo ei vielä varmista, että syöte on päivämäärä, koska muutkin merkit kuin numerot ovat sallittuja. Dateutil kelpuuttaa silloin esimerkiksi tällaisen kellonajan:
is_valid_date("10:10:30")
True
SuperOscarin ratkaisu on OK numeroiden tarkistamiseen ja erottamiseen, mutta niistä muodostuva päivämäärä pitää vielä validoida kuukausien ja päivien osalta, ja siihen on helpointa käyttää valmista ratkaisua, kuten tuota dateutilin parseria.
Ilman säännöllisiä lausekkeita onnistuisi dateutilin avulla näin:
from dateutil.parser import parse
def is_valid_date(d: str):
d = d.strip()
if len(d.strip()) == 8 and d.isdigit():
try:
parse(d)
return True
except:
return False
return False
Myös Pythonin standardikirjaston datetime.strptime (https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior) soveltuu tähän tapaukseen hyvin, koska syötteen formaatin voi rajata tarkemmin. Senkin kanssa on kuitenkin huomioitava, että formaatti "%Y%m%d" hyväksyy kuukaudet ja päivät sekä etunollan kanssa tai ilman, joten jos haluat validoida, että syötteessä on oikeasti kahdeksan numeroa, se on tehtävä erikseen.
datetime.strptime("202166", "%Y%m%d")
datetime.datetime(2021, 6, 6, 0, 0)
Alla oma ehdotukseni, joka toimii pelkällä standardikirjastolla ja ilman säännöllisiä lausekkeita. Oikeassa tuotantotason toteutuksessa käyttäjälle näkyvä teksti ja virheilmoitukset olisivat koodissa englanniksi ja ne käännettäisiin käyttäjän kielelle esim. gettextin (https://phrase.com/blog/posts/translate-python-gnu-gettext/) avulla.
from datetime import datetime
def parse_date(datestring: str):
d = datestring.strip()
if not (len(d) == 8 and d.isdigit()):
raise ValueError(f"päivämäärässä '{d}' ei ole kahdeksaa numeroa")
try:
return datetime.strptime(d, '%Y%m%d')
except ValueError as e:
raise ValueError(f"päivämäärä '{d}' ei täsmää formaattiin VVVVKKPP") from e
dateinput = ''
while dateinput != 'x':
dateinput = input('Anna päivämäärä: ')
try:
dt = parse_date(dateinput)
print(f'Hyväksytty päivämäärä: {dt}')
except ValueError as e:
print(f'Virhe: {e}')
-
Tämän säikeen ohjeilla ongelmaan löytyy jopa useita vähän erilaisia ratkaisuja.
Kiitos hyvistä ohjeista :)