2017-12-11 11 views
1

Ich habe ein Programm, das eine Textdatei als Eingabe nehmen und ein paar Dinge mit jeder Codezeile machen muss.Python - Bestätigung der Zeichenfolge aus Textdatei entspricht Format

Bevor ich jedoch irgendetwas mit der Textdatei machen kann, muss ich sicherstellen, dass jede Zeile einem bestimmten Format entspricht, das in diesem Fall der Name der Stadt wäre (was eine beliebige Zeichenfolge sein könnte, wäre keine echter Stadtname) gefolgt von einem ',' gefolgt von einer Zahl.

towna, 2,43

Dies ist, was ich im Moment haben, funktioniert es in Bezug auf die Herstellung der Funktionsprogramm aber es funktioniert nicht:

eine typische Linie würde wie folgt aussehen vergewissere dich, dass die Daten korrekt sind, also wenn es falsch ist (und es wird von Zeit zu Zeit sein), wird es mir einen Fehler geben. Ich habe ein paar Sachen über re.match gelesen, aber ich verstehe nicht, wie man es benutzt.

def read_file(fileName): 
    weatherFile = open(fileName) 
    for line in weatherFile: 
     stripped = line.replace(' ','') 
     pass #add data validation for file 
     town, rain = stripped.split(",") 
     if checkIfExists(town): 
      dataList.append({"city":town, "average_rainfall":float(rain.rstrip())}) 
     else: 
      print("It looks like {0} is on the list twice. Please ensure all towns in {1} only appear once and try again." .format(town, fileName)) 
      break 

Antwort

1

Bevor ich antworte, muss ich nur darauf hinweisen, dass Sie sich mit den Daten anlegen, bevor die Validierung überhaupt beginnt.

Was passiert, wenn der Benutzer Saint Jose,23.0 eingibt? Dann wird diese Zeile stripped = line.replace(' ','') in Ihrem Code durch SaintJose,23.0 ersetzen.

Wie auch immer, um dies zu lösen, ist es einfach und kann mit ein wenig Regex gemacht werden.

import re #python regex 

def read_file(fileName): 
    pattern = re.compile(r'(.*?),([-+]?(?:\d+\.\d*|\.?\d+)(?:[eE][-+]?\d+)?)') 
    with open(fileName) as weatherFile: 
     for line in weatherFile: 
      if not pattern.fullmatch(line.strip()): 
       continue 
      town, rain = line.split(",") 
      if checkIfExists(town): 
       dataList.append({"city":town, "average_rainfall":float(rain.rstrip())}) 
      else: 
       print("It looks like {0} is on the list twice. Please ensure all towns in {1} only appear once and try again." .format(town, fileName)) 
       break 

Sie können es machen, auch mit Generator Ausdrücke kürzer:

def read_file(fileName): 
    pattern = re.compile(r'(.*?),([-+]?(?:\d+\.\d*|\.?\d+)(?:[eE][-+]?\d+)?)') 
    with open(fileName) as weatherFile: 
     for town, rain in (line.split(",") for line in weatherFile if pattern.fullmatch(line.strip())) 
      if checkIfExists(town): 
       dataList.append({"city":town, "average_rainfall":float(rain.rstrip())}) 
      else: 
       print("It looks like {0} is on the list twice. Please ensure all towns in {1} only appear once and try again." .format(town, fileName)) 
       break 

dieser Zeile:

pattern = re.compile(r'(.*?),([-+]?(?:\d+\.\d*|\.?\d+)(?:[eE][-+]?\d+)?)') 

Kompiliert einen regulären Ausdruck für die Wiederverwendung. Dies ist bei komplexen Regex-Ausdrücken oft effizienter, so dass die Regex-Engine ihren internen Status nicht jedes Mal neu evaluieren und neu erstellen muss, wenn die Regex für den Abgleich verwendet wird.

Das Äquivalent für den Abgleich ohne vorherige Erstellung der regex wäre:

re.fullmatch(your_string_expression, r'(.*?),([-+]?(?:\d+\.\d*|\.?\d+)(?:[eE][-+]?\d+)?)') 

Die unheimlich aussehende regulären Ausdruck Sie sehen (dieses ([-+]?(?:\d+\.\d*|\.?\d+)(?:[eE][-+]?\d+)?)) Ints oder Schwimmer zum Abgleich verwendet wird, und aus diesem Grund ist es so lang. Python hat einige definiert here. Die eine, die ich hier verwendet, kombiniert übereinstimmende Ints und Floats, und Sie können ein Beispiel dafür sehen, dass es here verwendet.

+0

Danke, können Sie eine schnelle Erklärung geben, was das Muster = re.compile line bedeutet? – Mick

+0

@Mick, ich habe eine Erklärung hinzugefügt – smac89

0

Einige Leute, wenn sie mit einem Problem konfrontiert, denken: „Ich weiß, werde ich reguläre Ausdrücke verwenden.“ Jetzt haben sie zwei Probleme. (Jamie Zawinski)

Vergleichen Sie die (.*?),([-+]?(?:\d+\.\d*|\.?\d+)(?:[eE][-+]?\d+)?) regulären Ausdruck auf die folgende Validierungsdefinition unten vorgeschlagen:

validators = { 
    'City Name': [ 
     UniqueValidator() 
    ], 
    'Average Rainfall': [ 
     FloatValidator() 
    ] 
} 

Regular Expressions: Now You Have Two Problems


ich die Datei als CSV-Datei behandeln würde, die erlauben würde, Verwenden Sie eine csv module, um es zu lesen, und verwenden Sie auch ein CSV-Schema-Validierungspaket wie vladiate. Sie müssen keine Drittanbieter für dieses Problem verwenden, aber es bietet eine gute Trennung von Bedenken und ermöglicht eine schöne Validierungslogikdefinition.

Zum Beispiel, wenn Sie eine cities.csv-Datei mit dem folgenden Inhalt haben würde (man beachte die invalid_value in der „Durchschnittlicher Niederschlag“ Spalte):

City Name,Average Rainfall 
towna,2.43 
townb,1.01 
townc,invalid_value 

Und führen Sie den folgenden Code (beachten Sie, wie wir ein definieren einzigartig „Städtename“ Wert Anforderung und ein Gleitkommawert Validator für den „Durchschnittlicher Niederschlag“):

import csv 

from vladiate import Vlad 
from vladiate.validators import UniqueValidator, FloatValidator 
from vladiate.inputs import LocalFile 


class CityValidator(Vlad): 
    validators = { 
     'City Name': [ 
      UniqueValidator() 
     ], 
     'Average Rainfall': [ 
      FloatValidator() 
     ] 
    } 


def read_file(file_name): 
    CityValidator(source=LocalFile(file_name)).validate() 

    with open(file_name) as weather_file: 
     reader = csv.reader(weather_file) 
     for city_name, average_rainfall in reader: 
      print(city_name, average_rainfall) 


read_file("cities.csv") 

Sie gedruckt sehen würden:

City Name Average Rainfall 
towna 2.43 
townb 1.01 
townc invalid_value 

Validating CityValidator(source=LocalFile('cities.csv')) 
Failed :(
    FloatValidator failed 1 time(s) (33.3%) on field: 'Average Rainfall' 
    Invalid fields: ['invalid_value'] 
+0

Sorry, ich hätte wahrscheinlich hinzufügen sollen, das ist Teil eines Tests, so dass es eine Anforderung hat, dass es aus einer Textdatei gelesen wird. – Mick

+0

@Mick kein Problem, Sie können immer noch die Idee nutzen. Zum Beispiel würde 'von vladiate.inputs import String' erlauben, eine Zeichenkette und nicht eine Datei zu validieren. – alecxe

Verwandte Themen