Vraag & Antwoord

Webprogrammeren & scripting

fgetcsv(): never trust user input

13 antwoorden
  • Ik zit een beetje met een praktisch probleem. Via een externe partij worden Excelbestanden aangeleverd waar 2 relevante kolommen in zitten: een identifier (varchar) en een aantal punten. Dit wordt omgezet naar een CSV bestand (handmatig, de ene keer via Excel (diverse versies), de andere keer via Ooo Calc). Vervolgens laat ik dit inlezen in PHP met fgetcsv(). So far so good. Echter kom ik er vandaag achter dat het CSV bestand bij getallen boven de duizend er een punt (.) tussen zet. 1860 wordt dus bijvoorbeeld 1.860. En dan gaat het mis. Door die punt wordt de waarde niet meer als integer beschouwd, maar als float. Als ik dit nu laat opslaan in de MySQL database waar dat veld een integer is, wordt het afgerond naar 2. Dat levert dus foute data op. Nu kan ik natuurlijk eenvoudig een str_replace() uitvoeren om die punt eruit te filteren, maar dan zul je zien dat de volgende keer door landeninstellingen geen punt maar een komma gebruikt wordt. Of dat er in Excel ineens 1.860,00 (dus met 2 decimalen) is opgeslagen. Doordat ik geen invloed heb op hetgeen wat wordt aangeleverd en het inlezen ook gebeurt vanaf diverse computers/configuraties zit het me niet helemaal lekker om maar te vertrouwen op die simpele str_replace. Ook als ik dan die komma eruit wil filteren, dan zul je zien dat er ineens 186000 punten ingelezen worden. En da's dus ook niet goed. Tips zijn van harte welkom. :)
  • Kun je voor of tijdens het omzetten naar csv de eigenschappen van de kolom waarin het aantal punten staan niet wijzigen zodat het scheidingsteken voor duizendtallen niet meer wordt gebruikt?
  • Oh, dat kan wel en dat is voor mij ook geen enkel probleem. Het punt zit er echter in dat ik erop moet vertrouwen dat de [i:3385bf82e9]gebruiker[/i:3385bf82e9] dat ook doet. En stel [i:3385bf82e9]dat[/i:3385bf82e9] de gebruiker dit wil (bijvoorbeeld omdat ik een foutmelding teruggeef) dan moet de gebruiker óók nog eens weten [i:3385bf82e9]hoe[/i:3385bf82e9] dat moet in het specifieke programma dat gebruikt wordt. Praktijk heeft reeds uitgewezen dat ik hier niet op kan vertrouwen. En dus moet ik het op de een of andere manier afvangen.
  • Even voor de duidelijkheid: wordt de conversie naar csv door jou of door de aanleverende partij(en) gedaan?
  • Door de aanleverende partijen. Dat zorgt ook voor de diversiteit: een XLS omzetten naar CSV gaat in Excel toch weer anders dan in Calc.
  • Ik heb er een tijdje over nagedacht, maar ik denk niet dat er een goede oplossing is als je niet op z'n minst de "lokalisatie" kan bepalen. Want is [i:37656a191a]1,000[/i:37656a191a] duizend of één? Dat is nooit met zekerheid te zeggen als je niet meer informatie hebt. Je zou het kunnen ondervangen door alle getallen te analyseren, als je ergens iets tegenkomt als 1,000.0 dan weet je met welke conversie je te maken hebt, maar het is niet waterdicht en vrij complex. Dus je zult op de een of andere manier deze gegevens moeten krijgen of kunnen bepalen. Als dat lukt, dan kan je met een combinatie van regexps getallen omzetten naar de door jou gewenste notatie. - Bas
  • [quote:d68496c16d="BasHamar"]Ik heb er een tijdje over nagedacht, maar ik denk niet dat er een goede oplossing is als je niet op z'n minst de "lokalisatie" kan bepalen. Want is [i:d68496c16d]1,000[/i:d68496c16d] duizend of één? Dat is nooit met zekerheid te zeggen als je niet meer informatie hebt.[/quote:d68496c16d]Precies mijn probleem. [quote:d68496c16d="BasHamar"]Je zou het kunnen ondervangen door alle getallen te analyseren, als je ergens iets tegenkomt als 1,000.0 dan weet je met welke conversie je te maken hebt, maar het is niet waterdicht en vrij complex. Dus je zult op de een of andere manier deze gegevens moeten krijgen of kunnen bepalen. Als dat lukt, dan kan je met een combinatie van regexps getallen omzetten naar de door jou gewenste notatie.[/quote:d68496c16d]Op zich valt de complexiteit nog wel mee, met een regex kan ik een heel eind komen. Maar iets als 1,000 kan gewoon alles zijn, dus volgens mij is het simpelweg niet waterdicht te maken, doordat er gewoon teveel variabelen zijn. Nu hou ik erg van waterdicht, dus heb ik de afgelopen dagen nog regelmatig mijn hoofd hierover gebroken, maar ik kom er niet uit. In ieder geval bedankt voor het meedenken. :)
  • [quote:2c17b6c33c="Ger"]Door die punt wordt de waarde niet meer als integer beschouwd, maar als float. Als ik dit nu laat opslaan in de MySQL database waar dat veld een integer is, wordt het afgerond naar 2. Dat levert dus foute data op.[/quote:2c17b6c33c]Ik ken het soort data natuurlijk niet, maar kan je uit deze beschrijving niet de conclusie trekken dat een aangeleverd getal nooit een float kan zijn? Op basis daarvan moet het mogelijk zijn om het altijd goed te interpreteren. Verder kan je nadenken over het bereik van de waarden en eventuele verbanden tussen opeenvolgende waarden. Hoewel het misschien niet 100% dicht te timmeren valt zou je een hele goede [i:2c17b6c33c]educated guess[/i:2c17b6c33c] kunnen doen op basis van wat je weet over de invoer. - Bas
  • [quote:02431665f5="BasHamar"]Ik ken het soort data natuurlijk niet, maar kan je uit deze beschrijving niet de conclusie trekken dat een aangeleverd getal nooit een float kan zijn?[/quote:02431665f5]Was het maar zo... :( Een van de problemen is de enorme variatie. Soms zijn halve punten (dus floats) wel goed mogelijk, dan kun je zelfs 0.1 krijgen als valide waarde. Echter: een waarde van een miljoen is ook mogelijk, en dat maakt het zo vervelend.
  • Het is zoals Bas zegt, zonder lokalisatie weet je nooit de betekenis van de punt en komma. In de CSV zit dat gegeven niet. In de excel ook niet (daarom gaat het ook wel eens mis wanneer iemand met EN-UK locale een excel opent die met een EN-US of NL-NL locale is gemaakt). Echter, wanneer je de excel als XML 2003 worksheet opslaat, lijken alle getallen met de decimale punt worden opgeslagen, incl. het formaat waarin ze dienen worden getoond. Ik begrijp dat je hier niets aan hebt omdat het CSV-formaat lijkt vast te staan - ik ken ooo calc niet maar die zal vast geen Excel XML kunnen genereren. Een andere manier die ik recent in een project heb toegepast, is op basis van de gegevens zelf proberen af te leiden wat de lokale is - onder het motto: als je het gegeven niet mee krijgt, probeer je het zelf te achterhalen. Bevat een csv een getal van het formaat ###.###,### dan is het duidelijk, en andersom ook. Je weet het dan alleen niet indien de CSV alleen getallen met of een . of een , bevat maar niet beiden. Je kunt dan nog gebruik maken van het feit dat achter de 1000-tal punt/komma altijd 3 cijfers staan. In het geval het financiele gegevens betreft, kun je met de aanname van (maximaal) twee getallen achter de decimale komma/punt, dan alsnog e.e.a. achterhalen. Er blijven natuurlijk altijd werkbladen mogelijk waaruit de lokale niet te achterhalen is, je moet dan een aanname doen, of bij bijv. uploaden een foutmelding teruggeven of de lokale alsnog uitvragen. Met het gegeven kun je dan de werkbladen op de juiste manier inlezen, eventueel met conversiestap ertussen. Je moet het dus niet op getal-nivo bekijken maar op file-nivo - mag ik aannemen dat binnen 1 bestand slechts 1 locale van toepassing is? [edit: ik zie nu dat Bas ook reeds in deze richting hintte] Wellicht heb je hier iets aan. Mocht je hulp nodig hebben bij het samenstellen van de reg-exps, laat het dan even weten. Succes!
  • [quote:bf4b941fbb="marientje"]of bij bijv. uploaden een foutmelding teruggeven of de lokale alsnog uitvragen. Met het gegeven kun je dan de werkbladen op de juiste manier inlezen, eventueel met conversiestap ertussen. Je moet het dus niet op getal-nivo bekijken maar op file-nivo - mag ik aannemen dat binnen 1 bestand slechts 1 locale van toepassing is?[/quote:bf4b941fbb]In die richting zit ik nu te denken inderdaad. En ja, per bestand is maar 1 locale van toepassing. Al vraag ik me af hoe ik een totale noob (met alle respect, maar hun kwaliteiten liggen totaal niet op dit vlak) duidelijk maak wat ik wil weten en wat ze dan moeten aangeven. Maar da's een kwestie van juist verwoorden en niet zozeer van programmeren. :) [quote:bf4b941fbb="marientje"] Wellicht heb je hier iets aan. Mocht je hulp nodig hebben bij het samenstellen van de reg-exps, laat het dan even weten. Succes![/quote:bf4b941fbb]Bedankt, ik ga er zo wel uitkomen denk ik. Regexes kom ik over het algemeen ook wel uit, ooit nog een minicursusje over geschreven. :) PS: Leuk weer eens van je te lezen, ik kan je input altijd wel waarderen. :)
  • Misschien is het - bij wijze van hersengymnastiek - leuk om een lijst met aannames te maken waarmee je zelf de [i:0d242f9ee9]locale[/i:0d242f9ee9] kan bepalen? Dat komt ongetwijfeld in de toekomst nog wel eens van pas. Ik trap vast af: [list:0d242f9ee9] [*:0d242f9ee9]Wanneer een scheidingsteken meer dan één keer voorkomt is het om duizendtallen aan te geven. [*:0d242f9ee9]Wanneer beide scheidingstekens voorkomen is de "meest rechtse" het decimale teken. [*:0d242f9ee9]Wanneer er slechts één scheidingsteken is en het gevolgd wordt door één of twee cijfers dan is het het decimale scheidingsteken. [/list:u:0d242f9ee9] Dit zal vast en zeker uitgebreid kunnen worden. :) - Bas
  • Wat natuurlijk ook wel kan helpen: het bestand bevat altijd behoorlijk wat regels, meestal tussen de ± 100 en 300. Als je met de criteria zoals Bas hierboven opsomt (redelijk compleet voor de zekerheden) ook maar voor één van de waarden de locale kan bepalen, dan geldt die natuurlijk voor allemaal. En de kans dat je er bij een van die 100 tot 300 een waarde is die aan een van die criteria voldoet, is natuurlijk best groot. Waterdicht zal het niet snel worden, maar ik kan allicht voor spatwaterdicht gaan. ;)

Beantwoord deze vraag

Weet jij het antwoord op deze vraag? Registreer of meld je aan met je account

Dit is een gearchiveerde pagina. Antwoorden is niet meer mogelijk.