[Python] Extracting decimal and negative numbers from PDF, where decimals look like exponents...

Discussão em 'Python' iniciado por Stack, Novembro 24, 2025.

  1. Stack

    Stack Membro Participativo

    I am processing a set of PDF files using Python.
    From each PDF I need to extract several monetary amounts and then write them to an Excel file.

    The problem is that in the PDF the decimals are written with superscript digits after a comma, for example:


    • 310,⁷⁶⁹⁷


    • −25,³⁴

    These are normal decimal amounts (money), not powers.
    I want to convert them to:


    • 310.7697


    • -25.34

    I am using:


    • PyMuPDF (fitz) to read the PDF text (and fall back to Tesseract OCR if needed),


    • some regex + string cleaning to extract the amounts,


    • openpyxl to write the results to Excel.

    Below is a minimal reproducible example of the part that parses the amounts.
    The text I show here (sample text) is similar to what I get after OCR:

    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 <amount>'
    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


    After I collect the amounts, I format them and write them as text into Excel (using openpyxl) like this:

    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(".", ",")


    Then, I put amounts_str and total_str into Excel cells

    Problems

    The decimals are not always converted correctly. Sometimes a value like 310,⁷⁶⁹⁷ becomes 310.0 or even 3107697 instead of 310.7697.

    Negative amounts sometimes lose the minus sign. For example, −25,³⁴ in the PDF may end up as 25,34 (positive) in the Excel file.

    Expected behavior

    For the sample_text above I would expect:

    extract_amounts_from_period(sample_text) == [310.7697, -25.34]


    and later in Excel to see (as text):

    310,7697 + -25,3400

    Total cell: 285,4297

    What is a robust way in Python to:

    Parse these amounts from the PDF text (where decimals are written with superscript digits after a comma), and make sure the negative sign is always preserved, so that I get correct float values like 310.7697 and -25.34 before writing them to Excel?

    Continue reading...

Compartilhe esta Página