示例#1
0
    def __init__(self, format_string, strip_strings=True):
        """
        Sets the format string and determines how we will read and write
        strings using this format
        """
        self.format = format_string
        self.strip_strings = strip_strings  # for ease of copying

        # Define a function that processes all arguments prior to adding them to
        # the returned list. By default, do nothing, but this allows us to
        # optionally strip whitespace from strings.
        self.process_method = lambda x: x

        if FortranFormat.strre.match(format_string):
            rematch = FortranFormat.strre.match(format_string)
            # replace our write() method with write_string to force left-justify
            self.type, self.write = str, self._write_string
            nitems, itemlen = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.fmt = '%s'
            # See if we want to strip the strings
            if strip_strings: self.process_method = lambda x: x.strip()

        elif FortranFormat.intre.match(format_string):
            self.type = int
            rematch = FortranFormat.intre.match(format_string)
            nitems, itemlen = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.fmt = '%%%dd' % self.itemlen

        elif FortranFormat.floatre.match(format_string):
            self.type = float
            rematch = FortranFormat.floatre.match(format_string)
            nitems, itemlen, num_decimals = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.num_decimals = int(num_decimals)
            if 'F' in format_string.upper():
                self.fmt = '%%%s.%sF' % (self.itemlen, self.num_decimals)
            else:
                self.fmt = '%%%s.%sE' % (self.itemlen, self.num_decimals)

        elif FortranFormat.floatre2.match(format_string):
            self.type = float
            rematch = FortranFormat.floatre2.match(format_string)
            nitems, itemlen, num_decimals = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.num_decimals = int(num_decimals)
            if 'F' in format_string.upper():
                self.fmt = '%%%s.%sF' % (self.itemlen, self.num_decimals)
            else:
                self.fmt = '%%%s.%sE' % (self.itemlen, self.num_decimals)

        else:
            # We tried... now just use the fortranformat package
            self._reader = FortranRecordReader(format_string)
            self._writer = FortranRecordWriter(format_string)
            self.write = self._write_ffwriter
            self.read = self._read_ffreader
示例#2
0
    def __init__(self, format_string, strip_strings=True):
        """
        Sets the format string and determines how we will read and write
        strings using this format
        """
        self.format = format_string
        self.strip_strings = strip_strings # for ease of copying

        # Define a function that processes all arguments prior to adding them to
        # the returned list. By default, do nothing, but this allows us to
        # optionally strip whitespace from strings.
        self.process_method = lambda x: x

        if FortranFormat.strre.match(format_string):
            rematch = FortranFormat.strre.match(format_string)
            # replace our write() method with write_string to force left-justify
            self.type, self.write = str, self._write_string
            nitems, itemlen = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.fmt = '%s'
            # See if we want to strip the strings
            if strip_strings: self.process_method = lambda x: x.strip()

        elif FortranFormat.intre.match(format_string):
            self.type = int
            rematch = FortranFormat.intre.match(format_string)
            nitems, itemlen = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.fmt = '%%%dd' % self.itemlen

        elif FortranFormat.floatre.match(format_string):
            self.type = float
            rematch = FortranFormat.floatre.match(format_string)
            nitems, itemlen, num_decimals = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.num_decimals = int(num_decimals)
            if 'F' in format_string.upper():
                self.fmt = '%%%s.%sF' % (self.itemlen, self.num_decimals)
            else:
                self.fmt = '%%%s.%sE' % (self.itemlen, self.num_decimals)

        elif FortranFormat.floatre2.match(format_string):
            self.type = float
            rematch = FortranFormat.floatre2.match(format_string)
            nitems, itemlen, num_decimals = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.num_decimals = int(num_decimals)
            if 'F' in format_string.upper():
                self.fmt = '%%%s.%sF' % (self.itemlen, self.num_decimals)
            else:
                self.fmt = '%%%s.%sE' % (self.itemlen, self.num_decimals)

        else:
            # We tried... now just use the fortranformat package
            self._reader = FortranRecordReader(format_string)
            self._writer = FortranRecordWriter(format_string)
            self.write = self._write_ffwriter
            self.read = self._read_ffreader
示例#3
0
class FortranFormat(object):
    """
    Processes Fortran format strings according to the Fortran specification for
    such formats. This object handles reading and writing data with any valid
    Fortran format. It does this by using the `fortranformat` project
    [https://bitbucket.org/brendanarnold/py-fortranformat].

    However, while `fortranformat` is very general and adheres well to the
    standard, it is very slow. As a result, simple, common format strings have
    been optimized and processes reads and writes between 3 and 5 times faster.
    The format strings (case-insensitive) of the following form (where # can be
    replaced by any number) are optimized:
        - #E#.#
        - #D#.#
        - #F#.#
        - #(F#.#)
        - #a#
        - #I#

    Parameters
    ----------
    format_string : str
        The Fortran Format string to process
    strip_strings : bool=True
        If True, strings are stripped before being processed by stripping
        (only) trailing whitespace
    """

    strre = re.compile(r'(\d+)?a(\d+)$', re.I)
    intre = re.compile(r'(\d+)?i(\d+)$', re.I)
    floatre = re.compile(r'(\d+)?[edf](\d+)\.(\d+)$', re.I)
    floatre2 = re.compile(r'(\d+)?\([edf](\d+)\.(\d+)\)$', re.I)

    #===================================================

    def __init__(self, format_string, strip_strings=True):
        """
        Sets the format string and determines how we will read and write
        strings using this format
        """
        self.format = format_string
        self.strip_strings = strip_strings  # for ease of copying

        # Define a function that processes all arguments prior to adding them to
        # the returned list. By default, do nothing, but this allows us to
        # optionally strip whitespace from strings.
        self.process_method = lambda x: x

        if FortranFormat.strre.match(format_string):
            rematch = FortranFormat.strre.match(format_string)
            # replace our write() method with write_string to force left-justify
            self.type, self.write = str, self._write_string
            nitems, itemlen = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.fmt = '%s'
            # See if we want to strip the strings
            if strip_strings: self.process_method = lambda x: x.strip()

        elif FortranFormat.intre.match(format_string):
            self.type = int
            rematch = FortranFormat.intre.match(format_string)
            nitems, itemlen = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.fmt = '%%%dd' % self.itemlen

        elif FortranFormat.floatre.match(format_string):
            self.type = float
            rematch = FortranFormat.floatre.match(format_string)
            nitems, itemlen, num_decimals = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.num_decimals = int(num_decimals)
            if 'F' in format_string.upper():
                self.fmt = '%%%s.%sF' % (self.itemlen, self.num_decimals)
            else:
                self.fmt = '%%%s.%sE' % (self.itemlen, self.num_decimals)

        elif FortranFormat.floatre2.match(format_string):
            self.type = float
            rematch = FortranFormat.floatre2.match(format_string)
            nitems, itemlen, num_decimals = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.num_decimals = int(num_decimals)
            if 'F' in format_string.upper():
                self.fmt = '%%%s.%sF' % (self.itemlen, self.num_decimals)
            else:
                self.fmt = '%%%s.%sE' % (self.itemlen, self.num_decimals)

        else:
            # We tried... now just use the fortranformat package
            self._reader = FortranRecordReader(format_string)
            self._writer = FortranRecordWriter(format_string)
            self.write = self._write_ffwriter
            self.read = self._read_ffreader

    #===================================================

    def __copy__(self):
        return type(self)(self.format, self.strip_strings)

    #===================================================

    def __str__(self):
        return self.format

    def __repr__(self):
        return "<%s: %s>" % (type(self).__name__, self.format)

    #===================================================

    def write(self, items, dest):
        """
        Writes an iterable of data (or a single item) to the passed file-like
        object

        Parameters
        ----------
        items : iterable or single float/str/int
            These are the objects to write in this format. The types of each
            item should match the type specified in this Format for that
            argument
        dest : file or file-like
            This is the file to write the data to. It must have a `write` method
            or an AttributeError will be raised

        Notes
        -----
        This method may be replaced with _write_string (for #a#-style formats)
        or _write_ffwriter in the class initializer if no optimization is
        provided for this format, but the call signatures and behavior are the
        same for each of those functions.
        """
        if hasattr(items, '__iter__') and not isinstance(items, string_types):
            mod = self.nitems - 1
            for i, item in enumerate(items):
                dest.write(self.fmt % item)
                if i % self.nitems == mod:
                    dest.write('\n')
            if i % self.nitems != mod:
                dest.write('\n')
        else:
            dest.write(self.fmt % items)
            dest.write('\n')

    #===================================================

    def _write_string(self, items, dest):
        """ Writes a list/tuple of strings """
        if hasattr(items, '__iter__') and not isinstance(items, string_types):
            mod = self.nitems - 1
            for i, item in enumerate(items):
                dest.write((self.fmt % item).ljust(self.itemlen))
                if i % self.nitems == mod:
                    dest.write('\n')
            if i % self.nitems != mod:
                dest.write('\n')
        else:
            dest.write((self.fmt % items).ljust(self.itemlen))
            dest.write('\n')

    #===================================================

    def _read_nostrip(self, line):
        """
        Reads the line and returns converted data. Special-cased for flags that
        may contain 'blank' data. ugh.
        """
        line = line.rstrip('\n')
        nitems = int(ceil(len(line) / self.itemlen))
        ret = [0 for i in range(nitems)]
        start, end = 0, self.itemlen
        for i in range(nitems):
            ret[i] = self.process_method(self.type(line[start:end]))
            start = end
            end += self.itemlen
        return ret

    #===================================================

    def read(self, line):
        """ Reads the line and returns the converted data """
        line = line.rstrip()
        nitems = int(ceil(len(line) / self.itemlen))
        ret = [0 for i in range(nitems)]
        start, end = 0, self.itemlen
        for i in range(nitems):
            ret[i] = self.process_method(self.type(line[start:end]))
            start = end
            end += self.itemlen
        return ret

    #===================================================

    def _read_ffreader(self, line):
        """ Reads the line and returns the converted data """
        return self._reader.read(line.rstrip())

    #===================================================

    def _write_ffwriter(self, items, dest):
        dest.write('%s\n' % self._writer.write(items))

    #===================================================

    def __eq__(self, other):
        return (self.format == other.format
                and self.strip_strings == other.strip_strings)

    #===================================================

    def __hash__(self):
        return hash((self.format, self.strip_strings))

    #===================================================

    def __getstate__(self):
        return dict(format=self.format, strip_strings=self.strip_strings)

    def __setstate__(self, d):
        self.__init__(d['format'], d['strip_strings'])
示例#4
0
class FortranFormat(object):
    """
    Processes Fortran format strings according to the Fortran specification for
    such formats. This object handles reading and writing data with any valid
    Fortran format. It does this by using the `fortranformat` project
    [https://bitbucket.org/brendanarnold/py-fortranformat].

    However, while `fortranformat` is very general and adheres well to the
    standard, it is very slow. As a result, simple, common format strings have
    been optimized and processes reads and writes between 3 and 5 times faster.
    The format strings (case-insensitive) of the following form (where # can be
    replaced by any number) are optimized:
        - #E#.#
        - #D#.#
        - #F#.#
        - #(F#.#)
        - #a#
        - #I#

    Parameters
    ----------
    format_string : str
        The Fortran Format string to process
    strip_strings : bool=True
        If True, strings are stripped before being processed by stripping
        (only) trailing whitespace
    """

    strre = re.compile(r'(\d+)?a(\d+)$', re.I)
    intre = re.compile(r'(\d+)?i(\d+)$', re.I)
    floatre = re.compile(r'(\d+)?[edf](\d+)\.(\d+)$', re.I)
    floatre2 = re.compile(r'(\d+)?\([edf](\d+)\.(\d+)\)$', re.I)

    #===================================================

    def __init__(self, format_string, strip_strings=True):
        """
        Sets the format string and determines how we will read and write
        strings using this format
        """
        self.format = format_string
        self.strip_strings = strip_strings # for ease of copying

        # Define a function that processes all arguments prior to adding them to
        # the returned list. By default, do nothing, but this allows us to
        # optionally strip whitespace from strings.
        self.process_method = lambda x: x

        if FortranFormat.strre.match(format_string):
            rematch = FortranFormat.strre.match(format_string)
            # replace our write() method with write_string to force left-justify
            self.type, self.write = str, self._write_string
            nitems, itemlen = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.fmt = '%s'
            # See if we want to strip the strings
            if strip_strings: self.process_method = lambda x: x.strip()

        elif FortranFormat.intre.match(format_string):
            self.type = int
            rematch = FortranFormat.intre.match(format_string)
            nitems, itemlen = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.fmt = '%%%dd' % self.itemlen

        elif FortranFormat.floatre.match(format_string):
            self.type = float
            rematch = FortranFormat.floatre.match(format_string)
            nitems, itemlen, num_decimals = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.num_decimals = int(num_decimals)
            if 'F' in format_string.upper():
                self.fmt = '%%%s.%sF' % (self.itemlen, self.num_decimals)
            else:
                self.fmt = '%%%s.%sE' % (self.itemlen, self.num_decimals)

        elif FortranFormat.floatre2.match(format_string):
            self.type = float
            rematch = FortranFormat.floatre2.match(format_string)
            nitems, itemlen, num_decimals = rematch.groups()
            if nitems is None:
                self.nitems = 1
            else:
                self.nitems = int(nitems)
            self.itemlen = int(itemlen)
            self.num_decimals = int(num_decimals)
            if 'F' in format_string.upper():
                self.fmt = '%%%s.%sF' % (self.itemlen, self.num_decimals)
            else:
                self.fmt = '%%%s.%sE' % (self.itemlen, self.num_decimals)

        else:
            # We tried... now just use the fortranformat package
            self._reader = FortranRecordReader(format_string)
            self._writer = FortranRecordWriter(format_string)
            self.write = self._write_ffwriter
            self.read = self._read_ffreader

    #===================================================

    def __copy__(self):
        return type(self)(self.format, self.strip_strings)

    #===================================================

    def __str__(self):
        return self.format

    def __repr__(self):
        return "<%s: %s>" % (type(self).__name__, self.format)

    #===================================================

    def write(self, items, dest):
        """
        Writes an iterable of data (or a single item) to the passed file-like
        object

        Parameters
        ----------
        items : iterable or single float/str/int
            These are the objects to write in this format. The types of each
            item should match the type specified in this Format for that
            argument
        dest : file or file-like
            This is the file to write the data to. It must have a `write` method
            or an AttributeError will be raised

        Notes
        -----
        This method may be replaced with _write_string (for #a#-style formats)
        or _write_ffwriter in the class initializer if no optimization is
        provided for this format, but the call signatures and behavior are the
        same for each of those functions.
        """
        if hasattr(items, '__iter__') and not isinstance(items, string_types):
            mod = self.nitems - 1
            for i, item in enumerate(items):
                dest.write(self.fmt % item)
                if i % self.nitems == mod:
                    dest.write('\n')
            if i % self.nitems != mod:
                dest.write('\n')
        else:
            dest.write(self.fmt % item)
            dest.write('\n')

    #===================================================

    def _write_string(self, items, dest):
        """ Writes a list/tuple of strings """
        if hasattr(items, '__iter__') and not isinstance(items, string_types):
            mod = self.nitems - 1
            for i, item in enumerate(items):
                dest.write((self.fmt % item).ljust(self.itemlen))
                if i % self.nitems == mod:
                    dest.write('\n')
            if i % self.nitems != mod:
                dest.write('\n')
        else:
            dest.write((self.fmt % item).ljust(self.itemlen))
            dest.write('\n')

    #===================================================

    def _read_nostrip(self, line):
        """
        Reads the line and returns converted data. Special-cased for flags that
        may contain 'blank' data. ugh.
        """
        line = line.rstrip('\n')
        nitems = int(ceil(len(line) / self.itemlen))
        ret = [0 for i in range(nitems)]
        start, end = 0, self.itemlen
        for i in range(nitems):
            ret[i] = self.process_method(self.type(line[start:end]))
            start = end
            end += self.itemlen
        return ret

    #===================================================

    def read(self, line):
        """ Reads the line and returns the converted data """
        line = line.rstrip()
        nitems = int(ceil(len(line) / self.itemlen))
        ret = [0 for i in range(nitems)]
        start, end = 0, self.itemlen
        for i in range(nitems):
            ret[i] = self.process_method(self.type(line[start:end]))
            start = end
            end += self.itemlen
        return ret

    #===================================================

    def _read_ffreader(self, line):
        """ Reads the line and returns the converted data """
        return self._reader.read(line.rstrip())

    #===================================================

    def _write_ffwriter(self, items, dest):
        dest.write('%s\n' % self._writer.write(items))