Как мне найти последнюю непустую ячейку в Openpyxl? - PullRequest
0 голосов
/ 19 декабря 2018

Openpyxl может сказать мне max_row и max_col, «Используемый диапазон» листа Excel.Однако этот диапазон может включать ячейки без содержимого, если они были ранее выбраны или изменены.

Я хочу знать последний столбец и последнюю строку, которые имеют содержимое .

( Обсуждение для VBA, здесь. )

Например, если - здесь ниже пробелов в Использованном диапазоне и _ означает пробелы вне Использованного диапазона, Iхочу выбрать столбец, помеченный b, и строку, помеченную c, хотя Openpyxl и будет при вычислении max_row и max_col включать строки / столбцы с черточками.

aaaaa---__
aaaaa-b-__
aaaaa---__
--------__
--c-----__
--------__
__________
__________

1 Ответ

0 голосов
/ 19 декабря 2018

Я обнаружил, что openpyxl сообщает правильные значения для max_row и max_col для файлов, которые были сохранены, но если вы манипулируете содержимым листа и нуждаетесь в этих значениях перед сохранением, проблема по-прежнему сохраняется.

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

Рабочий листОбъект позволяет вам получить доступ к строкам индивидуально, но отдельные столбцы могут быть доступны только через .itercols().Будет ли это быстрее, чем сканирование всех столбцов в одном цикле, будет зависеть от того, насколько пустым будет лист.

from openpyxl import load_workbook
wb = load_workbook('test.xlsx')
wb.worksheets[0]['h6'] = None

print((wb.worksheets[0].max_row, wb.worksheets[0].max_column))

def find_edges(sheet):
    row = sheet.max_row
    while row > 0:
        cells = sheet[row]
        if all([cell.value is None for cell in cells]):
            row -= 1
        else:
            break
    if row == 0:
        return 0, 0

    column = sheet.max_column
    while column > 0:
        cells = next(sheet.iter_cols(min_col=column, max_col=column, max_row=row))
        if all([cell.value is None for cell in cells]):
            column -= 1
        else:
            break
    return row, column

print(find_edges(wb.worksheets[0]))

В этом примере я загружаю лист Excel, содержащий именно те данные, которые вы предложили, сзначение также все еще находится в H6, которое удаляется в строке 3.

Сначала оно печатает max_row и max_column, как сообщается openpyxl, а затем вызывает find_edges с листом, чтобынайдите фактические требуемые значения.

Для больших листов с очень небольшим объемом данных вы можете поэкспериментировать для ускорения с заменой сканирования столбцов, просто повторяя все столбцы, как только вы определили последнюю строку (для ограниченияsize), например, так:

columns = sheet.iter_cols(max_row=row)
column = 1
ci = 1
while True:
    try:
        cells = next(columns)
        if not all([cell.value is None for cell in cells]):
            column = ci
        ci += 1
    except StopIteration:
        break

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

Если вы предпочитаете короткий, а не читаемый:

def find_edges2(sheet):
    def row():
        for r in range(sheet.max_row, 0, -1):
            if not all([cell.value is None for cell in sheet[r]]):
               return r

    row = row()
    if not row:
        return 0, 0

    def column():
        for c in range(sheet.max_column, 0, -1):
            if not all([cell.value is None for cell in next(sheet.iter_cols(min_col=c, max_col=c, max_row=row))]):
                return c

    return row, column()
...