Як читати по одному символу з файлу в Python?


Відповіді:


89
with open(filename) as f:
  while True:
    c = f.read(1)
    if not c:
      print "End of file"
      break
    print "Read a character:", c

41
Оскільки це зчитування байта за раз, чи не буде воно невдалим для кодування, що не є ASCII?
Девід Шуанар

3
Запитання та відповіді змішують поняття символів та байтів. Якщо файл містить один байт на кодування символів, такий як Ascii та багато інших, тоді ви читаєте один символ, читаючи фрагмент розміром в один байт, інакше якщо кодування вимагає більше одного байта на символ, тоді ви просто читання одного байта, жодного символу.
Базель Шишані,

2
Це правильно. Тому я часто роблю, result = open(filename).read()а потім читаю resultсимвол за символом.
Шива

3
На запитання Девіда Чунарда: Цей фрагмент коректно працює в Python 3 з файлом у кодуванні UTF-8. Якщо у вас є, наприклад, файл у кодуванні Windows-1250, просто змініть перший рядок на with open(filename, encoding='Windows-1250') as f:
SergO

1
А щоб додати до SergO, open(filename, "r")vs open(filename, "rb")може призвести до різної кількості ітерацій (принаймні, говорячи про Python 3). Режим "r" може прочитати кілька байтів, щоб отримати, cякщо він потрапляє у відповідний спеціальний символ.
dcc310

40

спочатку відкрийте файл:

with open("filename") as fileobj:
    for line in fileobj:  
       for ch in line: 
           print ch

Погоджено, це здається більш пітонічним способом зробити це. Хіба це не подбало б про обробку кодування, яке не є ASCII?
Рон7,

16
Однією з причин, по якій ви можете читати файл по одному символу, є те, що файл занадто великий, щоб поміститися в пам'ять. Але відповідь вище передбачає, що кожен рядок може поміститися в пам’яті.
CS

Відредаговано, щоб відповідати Python 3.

16

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

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 - це лише розумний вибір, але, як завжди, вимірюйте спочатку.


9

Сам 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.

6
Я погоджуюся з настроями, але, можливо, це більше підходить як коментар до ОП?
Mike Boers

2
Може бути, але я думаю, що весь цей текст у коментарі виглядав би безладним.
Маттіас Нільссон,




2

Це також буде працювати:

with open("filename") as fileObj:
    for line in fileObj:  
        for ch in line:
            print(ch)

Він проходить через кожен рядок у файлі та кожен символ у кожному рядку.



0
f = open('hi.txt', 'w')
f.write('0123456789abcdef')
f.close()
f = open('hej.txt', 'r')
f.seek(12)
print f.read(1) # This will read just "c"

3
Ласкаво просимо до Stackoverflow! Вам слід уточнити - чому це відповідь?
davidkonrad

0

Щоб зробити доповнення, якщо ви читаєте файл, який містить рядок, який є vvvvery величезним, що може зіпсувати вашу пам'ять, ви можете розглянути їх читання в буфер, а потім дати кожен символ

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)

0
#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)

Незважаючи на те, що це може відповісти на питання авторів, у ньому відсутні деякі пояснювальні слова та посилання на документацію. Фрагменти необробленого коду не дуже корисні без деяких фраз навколо них. Можливо, вам також дуже корисно написати хорошу відповідь . Будь ласка, відредагуйте свою відповідь.
hellow

Вам не потрібен акторський склад для переліку.
user240515

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.