def load_file(self, path=None, first_data_line='auto', filters='*.*', text='Select a file, FACEPANTS.', default_directory=None, header_only=False, quiet=False):
        """
        This will clear the databox, load a file, storing the header info in 
        self.headers, and the data in self.columns

        If first_data_line="auto", then the first data line is assumed to be 
        the first line where all the elements are numbers.

        If you specify a first_data_line (index, starting at 0), the columns 
        need not be numbers. Everything above will be considered header 
        information and below will be data columns.

        In both cases, the line used to label the columns will always be the 
        last header line with the same (or more) number of elements as the 
        first data line.
        
        Parameters
        ----------
        path=None
            Path to the file. Using None will bring up a dialog.
        filters='*.*'            
            Filter for the file dialog (if path isn't specified)
        text='Select a file, FACEPANTS.'               
            Prompt on file dialog
        default_directory=None
            Which spinmob.settings key to use for the dialog's default 
            directory. Will create one if it doesn't already exist.
        header_only=False        
            Only load the header
        quiet=False              
            Don't print anything while loading.
        """
        
        # Set the default directory
        if default_directory is None: default_directory = self.directory

        # Ask user for a file to open
        if path == None:
            path = _s.dialogs.load(filters=filters,
                                        default_directory=self.directory,
                                        text=text)
        self.path = path

        if path is None:
            return None

        # make sure the file exists!
        if not _os.path.exists(path):
            if not quiet: print("ERROR: "+repr(path)+" does not exist.")
            return None

        # clear all the existing data
        self.clear()



        # First check if the file is SPINMOB_BINARY format!
        f = open(path, 'rb')

        # If this file is in SPINMOB_BINARY mode!
        if f.read(14).decode('utf-8') == 'SPINMOB_BINARY':
            
            # Next character is the delimiter
            self.delimiter = f.read(1).decode('utf-8')
            
            # Find the newline and get the data type
            s = ' '
            while not s[-1] == '\n': s = s+f.read(1).decode('utf-8')
            
            # Rest of the line is the binary dtype
            self.h(SPINMOB_BINARY = s.strip())

            # Now manually assemble the header lines to use in the analysis 
            # below. If I try readline() on the binary file, it will crash.
            lines = ['\n']
            
            # The end of the header is specified by 'SPINMOB_BINARY' on its own line.
            while not lines[-1] == 'SPINMOB_BINARY':
                
                # Get the next line, one character at a time.
                s = ' '
                while not s[-1] == '\n': s = s+f.read(1).decode('utf-8')
                
                # Okay we have it
                lines.append(s.strip())
            
            # Pop that last line, which should be 'SPINMOB_BINARY'
            lines.pop(-1)
            
            # We've reached the end of the header.
        
        # Close the binary read.
        f.close()
        


        # If we're not in binary mode, we can read all the lines and find
        # the delimiter as usual. (In binary mode, the delimiter is specified)
        if not 'SPINMOB_BINARY' in self.hkeys:

            # For non-binary files, we always read all the lines.
            f = open(path, 'r')
            lines = f.readlines()
            f.close()
    
            # Determine the delimiter
            if self.delimiter is None:
    
                # loop from the end of the file until we get something other than white space
                for n in range(len(lines)):
    
                    # strip away the white space
                    s = lines[-n-1].strip()
    
                    # if this line has any content
                    if len(s) > 0:
    
                        # try the different delimiter schemes until we find one
                        # that produces a number. Otherwise it's ambiguous.
                        if   _s.fun.is_a_number(s.split(None)[0]): self.delimiter = None
                        elif _s.fun.is_a_number(s.split(',') [0]): self.delimiter = ','
                        elif _s.fun.is_a_number(s.split(';') [0]): self.delimiter = ';'
    
                        # quit the loop!
                        break
        
        # Done reading lines and auto-determining delimiter.
            


        ##### Pares the header from lines
        self.header_lines = []

        for n in range(len(lines)):

            # split the line by the delimiter
            s = lines[n].strip().split(self.delimiter)

            # remove a trailing whitespace entry if it exists.
            if len(s) and s[-1].strip() == '': s.pop(-1)

            # first check and see if this is a data line (all elements are numbers)
            if first_data_line == "auto" and _s.fun.elements_are_numbers(s):

                # we've reached the first data line
                first_data_line = n

                # quit the header loop
                break;

            ### after that check, we know it's a header line

            # save the lines for the avid user.
            self.header_lines.append(lines[n].strip())

            # store the hkey and the rest of it
            if len(s):
                hkey      = s[0]
                if self.delimiter is None: remainder = ' '.join(s[1:])
                else:                      remainder = self.delimiter.join(s[1:])

                # first thing to try is simply evaluating the remaining string
                try: self.insert_header(hkey, eval(remainder, self._globals()))

                # otherwise store the string
                except: self.insert_header(hkey, remainder)

        # now we have a valid set of column ckeys one way or another, and we know first_data_line.
        if header_only: return self

        
        
        
        
        
        
        # Deal with the binary mode
        if 'SPINMOB_BINARY' in self.hkeys:
            
            # Read the binary file
            f = open(path, 'rb')
            s = f.read()
            f.close()
            
            # Get the delimiter for easier coding
            delimiter = self.delimiter.encode('utf-8')
            
            # Get the binary mode, e.g., 'float32'
            binary = self.h('SPINMOB_BINARY')
            
            # Number of bytes per element
            size = eval('_n.'+binary+'().itemsize', dict(_n=_n))
            
            # Location of first ckey
            start = s.find(b'SPINMOB_BINARY',14) + 15
            
            # Continue until we reach the last character.
            while not start >= len(s):
                
                # Get the location of the end of the ckey
                stop  = s.find(delimiter, start)
                
                # Woa, Nelly! We're at the end of the file.
                if stop == -1: break
                ckey  = s[start:stop].decode('utf-8').strip()
                
                # Get the array length
                start = stop+1
                stop  = s.find(b'\n', start)
                length = int(s[start:stop].strip())
                
                # Get the data!
                start = stop+1
                stop  = start+size*length
                self[ckey] = _n.fromstring(s[start:stop], binary)
                
                # Go to next ckey
                start = stop+1        
        
        
        
        # Otherwise we have a text file to load.
        else:
            # Make sure first_data_line isn't still 'auto'
            # which happens if there's no data, or if it's a binary file
            if first_data_line == "auto" and not 'SPINMOB_BINARY' in self.hkeys:
                if not quiet: print("\ndatabox.load_file(): Could not find a line of pure data! Perhaps check the delimiter?")
                return self
    
    
            ##### at this point we've found the first_data_line,
    
            # look for the ckeys
    
            # special case: no header
            if first_data_line == 0: ckeys = []
    
            # start by assuming it's the previous line
            else: ckeys = lines[first_data_line-1].strip().split(self.delimiter)
    
            # count the number of actual data columns for comparison
            column_count = len(lines[first_data_line].strip().split(self.delimiter))
    
            # check to see if ckeys is equal in length to the
            # number of data columns. If it isn't, it's a false ckeys line
            if len(ckeys) >= column_count:
                # it is close enough
                # if we have too many column keys, mention it
                while len(ckeys) > column_count:
                    extra = ckeys.pop(-1)
                    if not quiet: print("Extra ckey: "+extra)
    
            else:
                # it is an invalid ckeys line. Generate our own!
                ckeys = []
                for m in range(0, column_count): ckeys.append("c"+str(m))
    
            # last step with ckeys: make sure they're all different!
            self.ckeys = []
            while len(ckeys):
    
                # remove the key
                ckey = ckeys.pop(0)
    
                # if there is a duplicate
                if (ckey in ckeys) or (ckey in self.ckeys):
                    # increase the label index until it's unique
                    n=0
                    while (ckey+"_"+str(n) in ckeys) or (ckey+"_"+str(n) in self.ckeys): n+=1
                    ckey = ckey+"_"+str(n)
                self.ckeys.append(ckey)
    
            # initialize the columns arrays
            # I did benchmarks and there's not much improvement by using numpy-arrays here.
            for label in self.ckeys: self.columns[label] = []
    
            
            
            
            
            
            
            
            # Python 2 format
            #if _sys.version_info[0] == 2:
            try:
                def fix(x): return str(x.replace('i','j'))
                
                # loop over the remaining data lines, converting to numbers
                z = _n.genfromtxt((fix(x) for x in lines[first_data_line:]),
                                  delimiter=self.delimiter,
                                  dtype=_n.complex)
            
            # Python 3 format
            except:
                def fix(x): return bytearray(x.replace('i','j'), encoding='utf-8')        
    
                # loop over the remaining data lines, converting to numbers
                z = _n.genfromtxt((fix(x) for x in lines[first_data_line:]),
                                  delimiter=self.delimiter,
                                  dtype=_n.complex)
            
            # genfromtxt returns a 1D array if there is only one data line.
            # highly confusing behavior, numpy!
            if len(_n.shape(z)) == 1:
                # check to make sure the data file contains only 1 column of data
                rows_of_data = len(lines) - first_data_line
                if rows_of_data == 1: z = _n.array([z])
                else: z = _n.array(z)
    
            # fix for different behavior of genfromtxt on single columns
            if len(z.shape) == 2: z = z.transpose()
            else:                 z = [z]
    
            # Add all the columns
            for n in range(len(self.ckeys)):
    
                # if any of the imaginary components are non-zero, use complex
                if _n.any(_n.imag(z[n])): self[n] = z[n]
                else:                     self[n] = _n.real(z[n])

        # Done with loading in the columns of data

        
        
        # now, as an added bonus, rename some of the obnoxious headers
        for k in self.obnoxious_ckeys:
            if k in self.columns:
                self.columns[self.obnoxious_ckeys[k]] = self.columns[k]

        return self