Извлечение десятичных и отрицательных чисел из PDF, где десятичные дроби в PDF выглядят как показатели степени.Python

Программы на Python
Ответить
Anonymous
 Извлечение десятичных и отрицательных чисел из PDF, где десятичные дроби в PDF выглядят как показатели степени.

Сообщение Anonymous »

Я обрабатываю набор PDF-файлов с помощью Python.

Из каждого PDF-файла мне нужно извлечь несколько денежных сумм и затем записать их в файл Excel.
Проблема в том, что в PDF-файле десятичные дроби записываются с надстрочными цифрами после запятой, например: Это обычные десятичные суммы (деньги), а не полномочия.

Я хочу преобразовать их в: Я использую:
  • PyMuPDF (), чтобы прочитать текст PDF (и при необходимости вернуться к Tesseract OCR),
  • некоторая очистка регулярных выражений + строк для извлечения сумм,
  • openpyxl для записи результатов в Excel.
Ниже приведен минимальный воспроизводимый пример части, которая анализирует суммы.

Текст, который я показываю здесь (

Код: Выделить всё

sample text
) аналогично тому, что я получаю после оптического распознавания символов:

Код: Выделить всё

import re

def clean_and_convert_amount(amount_str: str) -> float:
"""
Cleans a numeric string like:
- '1 068,²⁷⁵⁶'
- '1 068,2756'
- '−2,2072'
and converts it to a float, preserving:
- the minus sign
- decimal digits, even if they are written as superscript.
"""
if not amount_str:
return 0.0

raw = str(amount_str).strip()

# normalize different minus characters to '-'
raw = raw.replace('−', '-').replace('–', '-')

# if there is any '-' we consider the number negative
is_negative = '-' in raw

# map superscript digits -> normal digits
superscript_map = {
'⁰': '0', 'ⁱ': '1', '¹': '1', '²': '2', '³': '3',
'⁴': '4', '⁵': '5', '⁶': '6', '⁷': '7', '⁸': '8', '⁹': '9',
'\u00B2': '2',   # ²
'\u00B3': '3',   # ³
'\u00B9': '1',   # ¹
}

def normalize_digit(ch: str) -> str:
if ch in superscript_map:
return superscript_map[ch]
if ch.isdigit():
return ch
return ''

# find decimal separator: comma (Romanian) or dot
sep_index = raw.find(',')
if sep_index == -1:
sep_index = raw.find('.')

# no separator -> treat everything as integer
if sep_index == -1:
digits_only = ''.join(normalize_digit(ch) for ch in raw)
if not digits_only:
return 0.0
value = float(digits_only)
return -value if is_negative else value

# split into integer and decimal part
int_raw = raw[:sep_index]
dec_raw = raw[sep_index + 1:]

# integer part: convert superscript/normal digits
integer_digits = ''.join(normalize_digit(ch) for ch in int_raw) or '0'

# decimal part: here we normally have the superscript digits
decimal_digits = ''.join(normalize_digit(ch) for ch in dec_raw) or '0'

number_str = f"{integer_digits}.{decimal_digits}"

try:
value = float(number_str)
except ValueError:
return 0.0

if is_negative:
value = -value

return value

def extract_amounts_from_period(text: str) -> list[float]:
"""
Extract amounts only from sentences of the form:
'din perioada ...  în cuantum de '
and ignore sentences containing 'a fost acoperita suma de'
(meaning 'the amount was covered').
"""
if not text:
return []

pattern = re.compile(
r"din\s+perioada\s+([^\n\r]+?)\s*(?:\n|\r\n)?\s*(?:în|in)\s+cuantum\s+de?\s*"
r"([-−–]?\s*[0-9\s,.\u00B2\u00B3\u00B9⁰¹²³⁴⁵⁶⁷⁸⁹]+)",
flags=re.IGNORECASE
)

lower_text = text.lower()
amounts: list[float] = []

for match in pattern.finditer(text):
start = match.start()
amount_str = match.group(2)

# Skip contexts like "a fost acoperita suma de ..."
context_before = lower_text[max(0, start - 150):start]
if "a fost acoperita suma de" in context_before or "a fost acoperită suma de" in context_before:
continue

value = clean_and_convert_amount(amount_str)
amounts.append(value)

return amouts
После сбора сумм я форматирую их и записываю в виде текста в Excel (используя openpyxl) следующим образом:

Код: Выделить всё

amounts = extract_amounts_from_period(text_from_pdf)
total = sum(amounts)

amounts_str = " + ".join(f"{v:.4f}".replace(".", ",") for v in amounts)
total_str = f"{total:.4f}".replace(".", ",")
Затем я помещаю суммы_str и total_str в ячейки Excel.
Проблемы
Десятичные дроби не всегда преобразуются правильно.
Иногда такое значение, как 310,⁷⁶⁹⁷, становится 310,0 или даже 3107697 вместо 310,7697.
Отрицательные суммы иногда теряют знак минус.
Например, -25,³⁴ в PDF-файле может оказаться 25,34 (положительным) в файле Excel.
Ожидаемое поведение
Для примера_текста выше я ожидаю:

Код: Выделить всё

extract_amounts_from_period(sample_text) == [310.7697, -25.34]
и позже в Excel, чтобы увидеть (в виде текста):
310,7697 + -25,3400
Всего ячеек: 285,4297
Каков надежный способ в Python:
Разобрать эти суммы из текста PDF (где десятичные дроби записываются с надстрочными цифрами после запятая) и убедиться, что отрицательный знак всегда сохраняется, чтобы я получал правильные значения с плавающей запятой, такие как 310,7697 и -25,34, прежде чем записывать их в Excel?

Подробнее здесь: https://stackoverflow.com/questions/798 ... like-expon
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»