@@ -79,7 +79,34 @@ def unpack(data: str) -> dict:
7979 return unpacked
8080
8181
82- def loads (adi : str ) -> dict :
82+ def loadi (adi : str , skip : int = 0 ) -> Iterator [dict ]:
83+ """Turn ADI formated string to header/records as an iterator over dict
84+ The skip option is useful if you want to watch a file for new records only. This saves processing time.
85+
86+ :param adi: the ADI data
87+ :param skip: skip first number of records (does not apply for header)
88+ :return: an iterator of records (first record is the header even if not available)
89+ """
90+
91+ record_data = adi
92+ if not adi .startswith ('<' ): # If a header is available
93+ hr_list = re .split (r'<[eE][oO][hH]>' , adi )
94+ if len (hr_list ) > 2 :
95+ raise TooMuchHeadersException ()
96+
97+ yield unpack (hr_list [0 ])
98+ record_data = hr_list [1 ]
99+ else : # Empty record for missing header
100+ yield {}
101+
102+ i = 0
103+ for rec in re .finditer (r'(.*?)<[eE][oO][rR]>' , record_data , re .S ):
104+ if i >= skip :
105+ yield unpack (rec .groups ()[0 ])
106+ i += 1
107+
108+
109+ def loads (adi : str , skip : int = 0 ) -> dict :
83110 """Turn ADI formated string to dictionary
84111 The parameters are converted to uppercase
85112
@@ -88,48 +115,50 @@ def loads(adi: str) -> dict:
88115 'RECORDS': [list of records]
89116 }
90117
118+ The skip option is useful if you want to watch a file for new records only. This saves processing time.
119+ In this case consider to use loadi() directly.
120+
91121 :param adi: the ADI data
122+ :param skip: skip first number of records (does not apply for header)
92123 :return: the ADI as a dict
93124 """
94125
95126 doc = {'HEADER' : None ,
96127 'RECORDS' : []
97128 }
98129
99- record_data = adi
100- if not adi .startswith ('<' ):
101- hr_list = re .split (r'<[eE][oO][hH]>' , adi )
102- if len (hr_list ) > 2 :
103- raise TooMuchHeadersException ()
104-
105- doc ['HEADER' ] = unpack (hr_list [0 ])
106- record_data = hr_list [1 ]
107-
108- rec_list = re .split (r'<[eE][oO][rR]>' , record_data )
109- for rec in rec_list :
110- if rec .strip ():
111- doc ['RECORDS' ].append (unpack (rec ))
130+ first = True
131+ for rec in loadi (adi , skip ):
132+ if first :
133+ doc ['HEADER' ] = rec
134+ first = False
135+ else :
136+ doc ['RECORDS' ].append (rec )
112137
113138 return doc
114139
115140
116- def load (file_name : str ) :
117- """Turn ADI formated string to dictionary
141+ def load (file_name : str , skip : int = 0 ) -> dict :
142+ """Load ADI formated file to dictionary
118143 The parameters are converted to uppercase
119144
120145 {
121146 'HEADER': None,
122147 'RECORDS': [list of records]
123148 }
124149
150+ The skip option is useful if you want to watch a file for new records only. This saves processing time.
151+ In this case consider to use loadi() directly.
152+
125153 :param file_name: the file name where the ADI data is stored
154+ :param skip: skip first number of records (does not apply for header)
126155 :return: the ADI as a dict
127156 """
128157
129158 with open (file_name , encoding = 'ascii' ) as af :
130159 data = af .read ()
131160
132- return loads (data )
161+ return loads (data , skip )
133162
134163
135164def pack (param : str , value : str , dtype : str = None ) -> str :
@@ -257,7 +286,7 @@ def dump(file_name: str, data_dict: dict, comment: str = 'ADIF export by ' + __p
257286 af .write (chunk )
258287
259288
260- __all__ = ['load' , 'loads' , 'dump' , 'dumps' ,
289+ __all__ = ['load' , 'loads' , 'loadi' , ' dump' , 'dumps' , 'dumpi ' ,
261290 'TooMuchHeadersException' , 'TagDefinitionException' ,
262291 'IllegalDataTypeException' , 'IllegalParameterException' ,
263292 'StringNotASCIIException' ]
0 commit comments