Как прочитать один символ за раз из файла в Python? - PullRequest
61 голосов
/ 07 июня 2010

Может кто-нибудь сказать мне, как я могу это сделать?

Ответы [ 12 ]

76 голосов
/ 07 июня 2010
with open(filename) as f:
  while True:
    c = f.read(1)
    if not c:
      print "End of file"
      break
    print "Read a character:", c
33 голосов
/ 25 декабря 2013

сначала откройте файл:

with open("filename") as fileobj:
    for line in fileobj:  
       for ch in line: 
           print ch
14 голосов
/ 06 октября 2014

Мне нравится принятый ответ: он прост и сделает работу. Я также хотел бы предложить альтернативную реализацию:

def chunks(filename, buffer_size=4096):
    """Reads `filename` in chunks of `buffer_size` bytes and yields each chunk
    until no more characters can be read; the last chunk will most likely have
    less than `buffer_size` bytes.

    :param str filename: Path to the file
    :param int buffer_size: Buffer size, in bytes (default is 4096)
    :return: Yields chunks of `buffer_size` size until exhausting the file
    :rtype: str

    """
    with open(filename, "rb") as fp:
        chunk = fp.read(buffer_size)
        while chunk:
            yield chunk
            chunk = fp.read(buffer_size)

def chars(filename, buffersize=4096):
    """Yields the contents of file `filename` character-by-character. Warning:
    will only work for encodings where one character is encoded as one byte.

    :param str filename: Path to the file
    :param int buffer_size: Buffer size for the underlying chunks,
    in bytes (default is 4096)
    :return: Yields the contents of `filename` character-by-character.
    :rtype: char

    """
    for chunk in chunks(filename, buffersize):
        for char in chunk:
            yield char

def main(buffersize, filenames):
    """Reads several files character by character and redirects their contents
    to `/dev/null`.

    """
    for filename in filenames:
        with open("/dev/null", "wb") as fp:
            for char in chars(filename, buffersize):
                fp.write(char)

if __name__ == "__main__":
    # Try reading several files varying the buffer size
    import sys
    buffersize = int(sys.argv[1])
    filenames  = sys.argv[2:]
    sys.exit(main(buffersize, filenames))

Код, который я предлагаю, по сути та же идея, что и ваш принятый ответ: прочитайте указанное количество байтов из файла. Разница в том, что сначала он читает хороший кусок данных (4006 - хорошее значение по умолчанию для X86, но вы можете попробовать 1024 или 8192; любой кратный размеру вашей страницы), а затем он возвращает символы в этом блоке по одному.

Код, который я представляю, может быть быстрее для больших файлов. Взять, к примеру, весь текст «Войны и мира» Толстого . Вот мои результаты синхронизации (Mac Book Pro, использующий OS X 10.7.4; so.py - это имя, которое я дал коду, который вставил):

$ time python so.py 1 2600.txt.utf-8
python so.py 1 2600.txt.utf-8  3.79s user 0.01s system 99% cpu 3.808 total
$ time python so.py 4096 2600.txt.utf-8
python so.py 4096 2600.txt.utf-8  1.31s user 0.01s system 99% cpu 1.318 total

Теперь: не принимайте размер буфера в 4096 как универсальную истину; посмотрите на результаты, которые я получаю для разных размеров (размер буфера (в байтах) и время стены (с)):

   2 2.726 
   4 1.948 
   8 1.693 
  16 1.534 
  32 1.525 
  64 1.398 
 128 1.432 
 256 1.377 
 512 1.347 
1024 1.442 
2048 1.316 
4096 1.318 

Как вы можете видеть, вы можете начать видеть выигрыш раньше (и мои сроки, вероятно, очень неточны); размер буфера является компромиссом между производительностью и памятью. Значение по умолчанию 4096 - это просто разумный выбор, но, как всегда, сначала измерьте.

8 голосов
/ 07 июня 2010

Python сам может помочь вам в этом, в интерактивном режиме:

>>> help(file.read)
Help on method_descriptor:

read(...)
    read([size]) -> read at most size bytes, returned as a string.

    If the size argument is negative or omitted, read until EOF is reached.
    Notice that when in non-blocking mode, less data than what was requested
    may be returned, even if no size parameter was given.
5 голосов
/ 07 июня 2010

Всего:

myfile = open(filename)
onecaracter = myfile.read(1)
4 голосов
/ 02 февраля 2014

Сегодня я выучил новую идиому для этого, наблюдая, как Рэймонд Хеттингер превращает код в красивый идиоматический Python :

import functools

with open(filename) as f:
    f_read_ch = functools.partial(f.read, 1)
    for ch in iter(f_read_ch, ''):
        print 'Read a character:', repr(ch) 
2 голосов
/ 07 июня 2010

Просто прочитайте один символ

f.read(1)
2 голосов
/ 07 июня 2010

Вы должны попробовать f.read(1), что, безусловно, правильно и правильно.

0 голосов
/ 10 сентября 2018
#reading out the file at once in a list and then printing one-by-one
f=open('file.txt')
for i in list(f.read()):
    print(i)
0 голосов
/ 11 марта 2017

В качестве дополнения, если вы читаете файл, содержащий очень большую строку, которая может сломать вашу память, вы можете рассмотреть их чтение в буфер и затем получить каждый символ

def read_char(inputfile, buffersize=10240):
    with open(inputfile, 'r') as f:
        while True:
            buf = f.read(buffersize)
            if not buf:
                break
            for char in buf:
                yield char
        yield '' #handle the scene that the file is empty

if __name__ == "__main__":
    for word in read_char('./very_large_file.txt'):
        process(char)
...