Ubuntu Suomen keskustelualueet

Ubuntun käyttö => Ohjelmointi, palvelimet ja muu edistyneempi käyttö => Aiheen aloitti: teele - 12.01.21 - klo:15.26

Otsikko: [ ratkaistu ] python syöterivin tarkistaminen
Kirjoitti: 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.


Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: SuperOscar - 12.01.21 - klo:19.24
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ä:

Koodia: [Valitse]
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:

Koodia: [Valitse]
    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.
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: teele - 12.01.21 - klo:20.37

Juuri noilla neuvoilla käpistely etenee

Koodia: [Valitse]
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 ....  :)
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: Jere Sumell - 14.01.21 - klo:11.06
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.
Koodia: [Valitse]
import datetime

x = datetime.datetime.now()
print(x)
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: SuperOscar - 14.01.21 - klo:11.44
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.
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: Jere Sumell - 14.01.21 - klo:15.03
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.
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: Jere Sumell - 14.01.21 - klo:15.15
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.
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: Jere Sumell - 14.01.21 - klo:15.33
Java-koodia
Koodia: [Valitse]
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:
Koodia: [Valitse]
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:

Koodia: [Valitse]
"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.


Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: JaniAlander - 14.01.21 - klo:17.57
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
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: Jere Sumell - 14.01.21 - klo:18.40

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.
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: JaniAlander - 14.01.21 - klo:20.51
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...
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: nm - 15.01.21 - klo:02.28
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:
Koodia: [Valitse]
pip3 install --user python-dateutil
Tai koko järjestelmän käyttöön:
Koodia: [Valitse]
sudo pip3 install python-dateutil
Esimerkki:
Koodia: [Valitse]
import dateutil.parser
d = dateutil.parser.parse("5:12:59 PM")  # Palauttaa datetime-objektin
print(d)
Lainaus
2021-01-15 17:12:59
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: JaniAlander - 15.01.21 - klo:13.45
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.
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: Tomin - 16.01.21 - klo:17.49
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
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: teele - 21.01.21 - klo:13.36

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

Koodia: [Valitse]
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ä  :)



Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: SuperOscar - 21.01.21 - klo:13.58
”Halutaan antaa” vai ”oletetaan annetuksi”? Jälkimmäinen on helpompi, esimerkiksi:

Koodia: [Valitse]
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:

Koodia: [Valitse]
try:
    year, month, day = valid_date.match(annettu_jono).groups()
except AttributeError:
    print('Virheellinen jono')
Otsikko: Vs: python syöterivin tarkistaminen
Kirjoitti: nm - 21.01.21 - klo:16.36
Koodia: [Valitse]
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:

Koodia: [Valitse]
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:

Koodia: [Valitse]
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.

Koodia: [Valitse]
datetime.strptime("202166", "%Y%m%d")
Lainaus
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.

Koodia: [Valitse]
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}')
Otsikko: Vs: [ ratkaistu ] python syöterivin tarkistaminen
Kirjoitti: teele - 23.01.21 - klo:18.36

Tämän säikeen ohjeilla ongelmaan löytyy jopa useita vähän erilaisia ratkaisuja.

Kiitos hyvistä ohjeista  :)