def read(self, **keys):
rows = keys.get('<STR_LIT>', None)<EOL>columns = keys.get('<STR_LIT>', None)<EOL>colnums = self._extract_colnums(columns)<EOL>if isinstance(colnums, int):<EOL><INDENT>return self.read_column(columns, **keys)<EOL><DEDENT>rows = self._extract_rows(rows)<EOL>if rows is None:<EOL><INDENT>nrows = self._info['<STR_LIT>']<EOL><DEDENT>else:<EOL><INDENT>nrows = rows.size<EOL><DEDENT>rows = self._extract_rows(rows)<EOL>dtype, offsets, isvar = self.get_rec_dtype(colnums=colnums, **keys)<EOL>array = numpy.zeros(nrows, dtype=dtype)<EOL>wnotvar, = numpy.where(isvar == False)  <EOL>if wnotvar.size > <NUM_LIT:0>:<EOL><INDENT>for i in wnotvar:<EOL><INDENT>colnum = colnums[i]<EOL>name = array.dtype.names[i]<EOL>a = array[name].copy()<EOL>self._FITS.read_column(self._ext+<NUM_LIT:1>, colnum+<NUM_LIT:1>, a, rows)<EOL>array[name] = a<EOL>del a<EOL><DEDENT><DEDENT>array = self._maybe_decode_fits_ascii_strings_to_unicode_py3(array)<EOL>wvar, = numpy.where(isvar == True)  <EOL>if wvar.size > <NUM_LIT:0>:<EOL><INDENT>for i in wvar:<EOL><INDENT>colnum = colnums[i]<EOL>name = array.dtype.names[i]<EOL>dlist = self._FITS.read_var_column_as_list(<EOL>self._ext+<NUM_LIT:1>, colnum+<NUM_LIT:1>, rows)<EOL>if (isinstance(dlist[<NUM_LIT:0>], str) or<EOL>(IS_PY3 and isinstance(dlist[<NUM_LIT:0>], bytes))):<EOL><INDENT>is_string = True<EOL><DEDENT>else:<EOL><INDENT>is_string = False<EOL><DEDENT>if array[name].dtype.descr[<NUM_LIT:0>][<NUM_LIT:1>][<NUM_LIT:1>] == '<STR_LIT:O>':<EOL><INDENT>for irow, item in enumerate(dlist):<EOL><INDENT>if IS_PY3 and isinstance(item, bytes):<EOL><INDENT>item = item.decode('<STR_LIT:ascii>')<EOL><DEDENT>array[name][irow] = item<EOL><DEDENT><DEDENT>else:<EOL><INDENT>for irow, item in enumerate(dlist):<EOL><INDENT>if IS_PY3 and isinstance(item, bytes):<EOL><INDENT>item = item.decode('<STR_LIT:ascii>')<EOL><DEDENT>if is_string:<EOL><INDENT>array[name][irow] = item<EOL><DEDENT>else:<EOL><INDENT>ncopy = len(item)<EOL>array[name][irow][<NUM_LIT:0>:ncopy] = item[:]<EOL><DEDENT><DEDENT><DEDENT><DEDENT><DEDENT>lower = keys.get('<STR_LIT>', False)<EOL>upper = keys.get('<STR_LIT>', False)<EOL>if self.lower or lower:<EOL><INDENT>_names_to_lower_if_recarray(array)<EOL><DEDENT>elif self.upper or upper:<EOL><INDENT>_names_to_upper_if_recarray(array)<EOL><DEDENT>self._maybe_trim_strings(array, **keys)<EOL>return array<EOL>
read a data from an ascii table HDU

By default, all rows are read.  Send rows= to select subsets of the
data.  Table data are read into a recarray for multiple columns,
plain array for a single column.

parameters
----------
columns: list/array
    An optional set of columns to read from table HDUs.  Can be string
    or number. If a sequence, a recarray is always returned.  If a
    scalar, an ordinary array is returned.
rows: list/array, optional
    An optional list of rows to read from table HDUS.  Default is to
    read all.
vstorage: string, optional
    Over-ride the default method to store variable length columns.  Can
    be 'fixed' or 'object'.  See docs on fitsio.FITS for details.
lower: bool, optional
    If True, force all columns names to lower case in output. Will over
    ride the lower= keyword from construction.
upper: bool, optional
    If True, force all columns names to upper case in output. Will over
    ride the lower= keyword from construction.