@@ -135,9 +135,25 @@ Iris' optimisation all together, and will take its chunksizes from Dask's behavi
135135
136136Character and String datatypes
137137------------------------------
138+ Summary
139+ ^^^^^^^
138140
139- In NetCDF
140- ^^^^^^^^^
141+ * Iris currently *only * fully supports fixed-width 'char' type data in netCDF variables
142+ * the 'string' type variable-width unicode strings will be added in a future release
143+ * 'char' variable data is represented as numpy string arrays in Iris objects
144+ * the numpy dtype is of the type "U<xx>", where <xx> is a character width.
145+ * the character width relates to the string *dimension * of the netCDF variable,
146+ which does not appear as a cube or coordinate dimension.
147+ * the dtype 'width' controls the length of string dimensions created when saving
148+ * Iris also uses a variable ``_Encoding `` attribute to enable storage of non-ascii
149+ characters in 'char' type arrays.
150+ * it appears as a regular attribute of the Iris object
151+ * it is not needed for ascii-only data
152+ * it is not needed to *read * 'utf-8' encoded data correctly
153+ * it **is ** required to *save * any non-ascii characters
154+
155+ In the NetCDF file format
156+ ^^^^^^^^^^^^^^^^^^^^^^^^^
141157In the NetCDF v4 implementation, there are three specific areas where the datatype and
142158storage characteristics of character data are relevant:
143159
@@ -155,43 +171,52 @@ storage characteristics of character data are relevant:
155171 "string dimension". If they contain *only * ascii character values, this is
156172 uncomplicated, but they may also be used to contain non-ascii data (i.e.
157173 including unicode characters). There is no universally defined agreement on how
158- to indicate a variable containing non-ascii data, but many datasets have used a
159- suitable ``_Encoding `` attribute .
174+ to indicate that bytes are encoded non-ascii data, but many older datasets have
175+ used a variable attribute ``_Encoding `` indicating the encoding name .
160176
161177The NetCDF documentation also mentions that an ``_Encoding `` attribute may be used to
162- represent non-ascii strings in a 'char' type array . However this is described as
163- "reserved for future use", and its valid values and effects are not explicitly defined.
178+ represent non-ascii strings. However this is described as "reserved for future use",
179+ and its valid values and effects are not explicitly defined.
164180
165181However, it is also notable that the standard ``ncgen `` and ``ncdump `` tools *do *
166182correctly interpret an ``_Encoding `` attribute in most cases, despite this not being an
167183"official" solution.
168184
169185
170- In CF
171- ^^^^^
172- The CF Conventions define a subset of "allowed" datatypes, and describe various data
173- elements stored as variables, such as data variables, auxiliary coordinates, cell
174- methods, etc.
186+ In the netCDF CF Conventions
187+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
188+ The ` CF Conventions < https://https://cfconventions.org/ >`_ define a subset of
189+ "allowed" datatypes, and various types of data elements represented by variables
190+ -- such as data variables, auxiliary coordinates, cell methods, etc.
175191
176192CF currently supports the use of either netcdf 'string' or 'char' arrays for any
177193variables.
194+ However, *historically *, CF has had more limited support, and also "unofficial
195+ conventions" have been used for string data encoded as bytes, which may be encountered
196+ in older datasets.
178197
179- However, historically CF has had more limited support, and also 'unofficial'
180- conventions for the use of 'char' arrays have been used which may be encountered in
181- older datasets.
198+ **Prior to v1.8 **, CF required to use 'char' type only, without providing
199+ *any official means * of representing non-ascii data.
182200
183- Since v1.8, CF has allowed the use of 'string' data in all variables, but prior to that
184- it was required to use 'char' type only, without providing any official means of storing
185- non-ascii data .
201+ ** Since v1.8 ** , CF has allowed the use of 'string' data in all variables.
202+ However, up to v1.12 there was still no official way of encoding non-ascii data in
203+ 'char' arrays .
186204
187- Since v1.12, CF also mandates a *default * assumption of utf-8 encoding to store non-ascii
188- data in 'char' form, but it also notes that some data in the past has used an
189- ``_Encoding `` attribute -- though was never an official CF usage.
205+ ** Since v1.12 ** , CF now mandates a *default * assumption of utf-8 encoding to store
206+ non-ascii data in 'char' form, but it also notes that some data in the past has used an
207+ ``_Encoding `` attribute -- though this was never an official CF usage.
190208
191- Where strings are stored as 'char' type, the array must have a "string dimension",
192- which is a normal file dimension. Thus, these strings always have a *fixed byte width *.
193- (However, that is not the same as a fixed *character * width, since in most encodings
194- non-ascii characters require more bytes to store.)
209+ Characteristics
210+ ~~~~~~~~~~~~~~~
211+ Where strings are stored as 'char' type, which is the more common traditional approach,
212+ the array must have a "string dimension", which is a normal file dimension. Thus, these
213+ strings always have a *fixed byte width *. (However, that is not the same as a fixed
214+ *character * width, since in most encodings non-ascii characters require more bytes to
215+ store).
216+
217+ Although the variable-length 'string' data is now supported in CF, the use of
218+ fixed-width 'char' arrays is obviously more efficient for storage and access, and it is
219+ still the most common approach in practice.
195220
196221
197222In the netCDF4 Python module
@@ -213,47 +238,65 @@ In the netCDF4 Python module
213238
214239In Iris
215240^^^^^^^
216- In Iris, the 'string' data type is supported at present, though this is planned for
217- future releases. See the following section `Variable-length datatypes `_ for
241+ In Iris, ** the 'string' data type is not supported at present ** , though this is planned
242+ for future releases. See the following section `Variable-length datatypes `_ for
218243an interim solution enabling you at least to *load * variable-length string data.
219244
220245Iris stores string data in arrays of dtype "U<xx>", where <xx> is a maximum character
221246width. However, this data is currently **only ** read and written in netCDF files as
222- 'char' type variables.
247+ 'char' type variables (i.e. byte arrays) .
223248
224- Iris provides a set of valid encodings for non-ascii data :
225- "ascii", "utf8", "utf16" and "utf32". These (or valid aliases) will appear in the
249+ Iris supports a specific set of valid encodings for non-ascii data :
250+ "ascii", "utf8", "utf16" and "utf32". These (or aliases) will appear in the
226251``_Encoding `` attribute of a file variable, and likewise as an attribute of the
227252corresponding Iris component object (e.g. cube or coordinate).
228253
229- **On loading **, if there is a valid ``_Encoding `` attribute this is used to decode the data,
230- otherwise a default encoding of "utf8" is applied: This works transparently if only
231- ascii bytes are present, and also allows the ``_Encoding `` attribute to be omitted as
232- long as utf8 was used.
233-
234- **On saving **, any string data with only ascii characters does not require an
235- ``_Encoding `` attribute. However if there are non-ascii bytes, and no ``_Encoding ``
236- attribute, then an error will be raised.
254+ When loading
255+ ~~~~~~~~~~~~
256+ If there is a valid ``_Encoding `` attribute this is used to decode the
257+ data, otherwise a default encoding of "utf8" is applied: This works transparently when
258+ only ascii characters are present, and also allows the ``_Encoding `` attribute to be
259+ omitted as long as utf8 was used. An invalid or unsupported encoding name will be
260+ ignored, with a warning, but the attribute will still be added to the Iris component
261+ object.
262+
263+ When saving
264+ ~~~~~~~~~~~
265+ Any string data with only ascii characters does not require an ``_Encoding `` attribute.
266+ However if there are any non-ascii characters, and no ``_Encoding ``
267+ attribute, then an error will be raised. An invalid or unsupported encoding name will
268+ be ignored, with a warning, but the attribute will still be stored to the file.
237269
238270So effectively, the **"default" encodings are 'utf8' for load and 'ascii' for save **.
239271
272+ String width dimensions
273+ ~~~~~~~~~~~~~~~~~~~~~~~
240274For each valid encoding there is a definite relation between the string dimension length
241- in the file (actually, the number of *bytes *), and the maximum character length in the
242- array dtype, i.e. the "<xx>" in the "U<xx>" dtype.
275+ in the file (actually, the number of *bytes *), and the maximum character length, aka
276+ string width, in the array dtype : i.e. the "<xx>" in the "U<xx>" dtype.
243277
244- The lengths of string dimensions created on write are calculated as follows:
278+ The ** lengths of string dimensions created on write ** are calculated as follows:
245279
246280* ascii : n-bytes = n-characters
247281* utf8 : n-bytes = n-characters
248282* utf16 : n-bytes = 2 * (n-characters + 1)
249283* utf32 : n-bytes = 4 * (n-characters + 1)
250284
251285For reading, the inverse relations are applied to determine the '"U<xx>"' dtype in which
252- the data is presented. This will round-trip correctly, i.e. is unchanged if written and
253- then read back.
286+ the data is presented. This will always round-trip correctly, i.e. the dimension length
287+ is unchanged if data is read and then written back.
288+
289+ .. note ::
290+
291+ When processing string arrays, Numpy does not routinely preserve the "<xx>" width part
292+ of "U<xx>" type data, but some operations will reduce it to the maximum width
293+ occurring. Thus, it may be necessary to explicitly re-assert the desired "string
294+ width" before saving with computed string arrays.
295+ E.G. ``coord.points = coord.core_points().astype("U40") ``
296+
254297
255- For 'ascii' and 'utf32' this relationship is simple + fixed, but for 'utf8' and
256- 'utf16', the number of encoded bytes depends on the actual characters present
298+ For 'ascii' and 'utf32' this character-to-byte relationship is simple + fixed, but for
299+ 'utf8' and ' utf16', the number of encoded bytes depends on the actual characters present
257300**and can exceed the numbers given above **. If any string in the *actual * data encodes
258301to more bytes than the above-calculated string dimension, then Iris will raise an
259302:class: `iris.exceptions.TranslationError `. In this case, the user must **explicitly
0 commit comments