Эффективный способ чтения целых чисел из файла

Я хотел бы прочитать все целые числа из файла в один список. Все номера разделяются пробелом (одним или несколькими) или символом конца строки (один или несколько). Каков наиболее эффективный и/или элегантный способ сделать это? У меня есть два решения, но я не знаю, хороши они или нет.

  • Проверка цифр:

    for line in open("foo.txt", "r"):
        for i in line.strip().split(' '):
            if i.isdigit():
                my_list.append(int(i))
    
  • Работа с исключениями:

    for line in open("foo.txt", "r"):
        for i in line:
            try:
                my_list.append(int(i))
            except ValueError:
                pass
    

Пример данных:

1   2     3
 4 56
    789         
9          91 56   

 10 
11 

Ответ 1

Эффективным способом сделать это будет ваш первый метод с небольшим изменением использования инструкции with для открытия файла, пример -

with open("foo.txt", "r") as f:
    for line in f:
        for i in line.split():
            if i.isdigit():
                my_list.append(int(i))

Сроки выполнения тестов с использованием других методов -

Функции -

def func1():
    my_list = []
    for line in open("foo.txt", "r"):
        for i in line.strip().split(' '):
            if i.isdigit():
                my_list.append(int(i))
    return my_list

def func1_1():
    return [int(i) for line in open("foo.txt", "r") for i in line.strip().split(' ') if i.isdigit()]

def func1_3():
    my_list = []
    with open("foo.txt", "r") as f:
        for line in f:
            for i in line.split():
                if i.isdigit():
                    my_list.append(int(i))
    return my_list

def func2():            
    my_list = []            
    for line in open("foo.txt", "r"):
        for i in line.split():
            try:
                my_list.append(int(i))
            except ValueError:
                pass
    return my_list

def func3():
    my_list = []
    with open("foo.txt","r") as f:
        cf = csv.reader(f, delimiter=' ')
        for row in cf:
            my_list.extend([int(i) for i in row if i.isdigit()])
    return my_list

Результаты тестов времени -

In [25]: timeit func1()
The slowest run took 4.70 times longer than the fastest. This could mean that an intermediate result is being cached
1000 loops, best of 3: 204 µs per loop

In [26]: timeit func1_1()
The slowest run took 4.39 times longer than the fastest. This could mean that an intermediate result is being cached
1000 loops, best of 3: 207 µs per loop

In [27]: timeit func1_3()
The slowest run took 5.46 times longer than the fastest. This could mean that an intermediate result is being cached
10000 loops, best of 3: 191 µs per loop

In [28]: timeit func2()
The slowest run took 4.09 times longer than the fastest. This could mean that an intermediate result is being cached
1000 loops, best of 3: 212 µs per loop

In [34]: timeit func3()
The slowest run took 4.38 times longer than the fastest. This could mean that an intermediate result is being cached
10000 loops, best of 3: 202 µs per loop

Учитывая методы, которые хранят данные в списке, я считаю, что func1_3() выше быстрее (как показано в timeit).


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


ОБНОВЛЕНИЕ. Как было сказано в комментариях, func2() быстрее, чем func1_3() (хотя в моей системе он никогда не был быстрее, чем func1_3() даже для целых чисел), обновлялся foo.txt содержать вещи, отличные от чисел, и принимать временные тесты -

foo.txt

1 2 10 11
asd dd
 dds asda
22 44 32 11   23
dd dsa dds
21 12
12
33
45
dds
asdas
dasdasd dasd das d asda sda

Тест -

In [13]: %timeit func1_3()
The slowest run took 6.17 times longer than the fastest. This could mean that an intermediate result is being cached
1000 loops, best of 3: 210 µs per loop

In [14]: %timeit func2()
1000 loops, best of 3: 279 µs per loop

In [15]: %timeit func1_3()
1000 loops, best of 3: 213 µs per loop

In [16]: %timeit func2()
1000 loops, best of 3: 273 µs per loop

Ответ 2

Это довольно легко, если вы можете прочитать весь файл в виде строки. (т.е. он не слишком большой для этого)

fileStr = open('foo.txt').read().split() 
integers = [int(x) for x in fileStr if x.isdigit()]

read() превращает его в длинную строку, а split разделяется на список строк на основе пробелов (т.е. пробелы и символы новой строки). Таким образом, вы можете комбинировать это со списком, который преобразует их в целые числа, если они являются цифрами.

Как отметил Бакуриу, если в файле гарантированы только пробелы и цифры, то вам не нужно проверять isdigit(). Использовать list(map(int, open('foo.txt').read().split())) было бы достаточно в этом случае. Этот метод будет вызывать ошибки, если что-либо является недопустимым целым числом, тогда как другое будет пропускать все, что не является признанной цифрой.

Ответ 3

Спасибо всем. Я смешал некоторые решения, которые вы опубликовали. Мне это кажется очень хорошим:

with open("foo.txt","r") as f:
    my_list = [int(i)  for line in f for i in line.split() if i.isdigit()]

Ответ 4

Вы можете сделать это так, используя понимание списка

my_list = [int(i)  for j in open("1.txt","r") for i in j.strip().split(" ") if i.isdigit()]

Или with open() method:

with open("1.txt","r") as f:
    my_list = [int(i)  for j in f for i in j.strip().split(" ") if i.isdigit()]

процесс:

1. Сначала вы будете выполнять итерацию по строке

2. Затем вы будете итерировать слова и увидеть их цифрами, если мы добавим их в список

изменить

Вам нужно добавить strip() в строку, потому что каждый конец строки (кроме последней строки) будет иметь новое пространство строк ( "\n" ) в них, и вы пытаетесь is.digit("number\n") you will get false

т.е.)

>>> "1\n".isdigit()
False

edit2:

Ввод:

1
qw 2
23 we 32

Данные файла при чтении:

a=open("1.txt","r")

repr(a.read())
"'1\\nqw 2\\n23 we 32'"

Вы можете увидеть новую строку "\n", это повлияет на процесс

Когда я запускаю функцию без strip(), она не примет 1 and 2 как цифру, потому что она состоит из новых символов строки

my_list = [int(i)  for j in open("1.txt","r") for i in j.split(" ") if i.isdigit()]
my_list
[23, 32]

Из вывода видно, что 1 и 2 отсутствуют. Этого можно избежать, если мы использовали strip()

Ответ 5

почему бы не использовать ключевое слово yield? код будет выглядеть как...

def readInt():
    for line in open("foo.txt", "r"):
        for i in line.strip().split(' '):
            if i.isdigit():
                yield int(i)

то вы можете прочитать

    for num in readInt():
        list.append(num)

Ответ 6

my_list = []
with open('foo.txt') as f:
    for line in f:
        for s in line.split():
            try:
                my_list.append(int(s))
            except ValueError:
                pass

Ответ 7

Попробуйте следующее:

with open('file.txt') as f:
    nums = []
    for l in f:
        l = l.strip()
        nums.extend([int(i) for i in l.split() if i.isdigit() and l])

l.strip() требуется выше, если присутствуют символы новой строки ('\n'), поскольку i.isdigit('6\n') не будет работать.

list.extend пригодится здесь

and l в конце обязательно удаляет любой пустой результат списка

str.split по умолчанию разделяет пробелы. А блок with автоматически закроет файл после выполнения кода внутри. Я также использовал список понятий

Ответ 8

Это был самый быстрый способ, который я нашел:

import re
regex = re.compile(r"\D+")

with open("foo.txt", "r") as f:
    my_list = list(map(int, regex.split(f.read())))

Хотя результаты могут зависеть от размера файла.