Ошибка could not convert string to float значит одно: в float() попала строка, которую Python не считает записью числа. В русских данных причина почти всегда одна и та же — десятичная запятая: float("77.59") возвращает 77.59, а float("77,59") падает, потому что разделителем дробной части Python признаёт только точку.
Если ты пришёл по запросу «could not convert string to float python запятая» — да, минимальный фикс это price.replace(",", "."). Но одной замены хватает не всегда. Дальше по частоте идут пустая строка из хвоста файла, невидимый BOM в первой ячейке выгрузки из Excel и неразрывный пробел в разрядах вроде 1 234,50. Разберём по одной причине за раз — от того, откуда строка пришла, а не от абстрактного правила.
Что означает ошибка could not convert string to float?
Это ValueError: функция float() получила строку и не смогла разобрать её как число. Python принимает только строгий формат — необязательный знак, цифры, точка как разделитель дробной части и, при желании, экспонента вроде 1e3. Всё остальное — запятая, значок валюты, буквы, пустота — приводит к исключению. Самое полезное в сообщении то, что оно печатает саму проблемную строку в кавычках: это готовая улика, по которой причина определяется за секунду.
price = "77,59"
print(float(price))
Последняя строка вывода:
ValueError: could not convert string to float: '77,59'
Читай то, что стоит в кавычках после двоеточия:
'77,59' — запятая вместо точки;
'' — пришла пустая строка;
'\ufeff77,59' — в начале BOM из файла;
'1\xa0234,50' — неразрывный пробел между разрядами;
'12.5 руб.' — в строку затесался текст.
Если ты видишь в кавычках \ufeff или \xa0 — символ невидимый, глазами его в терминале не найти. Поэтому первое действие при непонятной строке всегда одно: print(repr(raw)).
Чтобы эту строку прочитать, сообщение должно дожить до твоих глаз. При запуске двойным кликом консольное окно гаснет вместе с процессом, и ValueError исчезает вместе с ним — тогда сначала запусти файл из cmd, как разобрано в статье скрипт закрывается сразу после запуска.
Почему float('12,5') выдаёт ошибку, а float('12.5') работает?
Потому что float() разбирает строку по правилам синтаксиса самого Python, а не по правилам твоей локали. В исходном коде дробная часть отделяется точкой — x = 12.5, — и функция использует ровно тот же парсер. Запятая для него не разделитель, а посторонний символ, из-за которого вся строка перестаёт быть числом. Запись 77,59 приходит из Excel, 1С, банковских выгрузок и просто из input(), поэтому именно она даёт эту ошибку чаще всего.
| Строка |
float(...) |
Почему так |
"77.59" |
77.59 |
точка — единственный разделитель дробной части |
"77,59" |
ValueError |
запятая не разбирается |
"1e3" |
1000.0 |
экспоненциальная запись допустима |
"1_000.5" |
1000.5 |
подчёркивания разрешены, как в литералах |
" 12.5\n" |
12.5 |
пробелы и перенос по краям отбрасываются |
"" |
ValueError |
пустая строка |
"nan" |
nan |
это валидное значение float |
Короче говоря, float не принимает число с запятой ни в Python 3.12, ни в любой другой версии. Чтобы преобразовать строку с запятой в число, достаточно поменять разделитель:
price = "77,59"
print(float(price.replace(",", ".")))
Какие вообще строки Python считает числами и чем float() отличается от int() — коротко разобрано в справочнике, в разделе про преобразование типов.
Когда одной замены replace(',', '.') уже мало
Замена лечит запятую и больше ничего. Настоящая выгрузка приносит ещё и разряды, причём разделитель разрядов бывает двух видов: обычный пробел и неразрывный пробел \xa0 (U+00A0), который Excel и веб-страницы вставляют, чтобы число не разорвалось при переносе строки. На экране они выглядят абсолютно одинаково.
raw = " 1\u00a0234,50\n"
print(repr(raw))
' 1\xa0234,50\n'
Тонкость: float() сам отбрасывает пробельные символы по краям строки, включая неразрывный, но не трогает те, что стоят внутри числа. Значит, чистить надо весь набор сразу:
def to_float(raw: str) -> float:
cleaned = raw.replace("\ufeff", "").replace("\u00a0", "").replace(" ", "")
return float(cleaned.replace(",", "."))
print(to_float("77,59"))
print(to_float(" 1\u00a0234,50\n"))
print(to_float("12.5"))
77.59
1234.5
12.5
| Что в строке |
Только replace(",", ".") |
Полная чистка |
"77,59" |
77.59 |
77.59 |
"1 234,50" (обычный пробел) |
ValueError |
1234.5 |
"1\xa0234,50" (неразрывный) |
ValueError |
1234.5 |
"\ufeff8,0" (BOM) |
ValueError |
8.0 |
Обрати внимание на третью строку вывода: to_float("12.5") тоже работает. Замена запятой на точку безопасна для строк, где запятой нет, поэтому одну функцию можно применять ко всей колонке, не разделяя данные на «русские» и «английские».
Соблазн почистить строку через strip() или rstrip() тут велик, но rstrip() — отдельная ловушка: он снимает набор символов, а не окончание строки. Как это молча съедает лишние буквы, разобрано в статье про удаление расширения файла.
Пустая строка и перенос: что из них правда ломает float()
Народное объяснение «мешает \n в конце» неверно. float() сам отбрасывает пробельные символы по краям, поэтому float("12.5\n") спокойно вернёт 12.5. Ломает не перенос, а пустая строка, которая из этого переноса рождается: если файл заканчивается символом \n, то при разбиении по строкам последним элементом окажется ''.
text = "77,59\n1 234,50\n\n8,0\n"
for line in text.split("\n"):
print(repr(line))
'77,59'
'1 234,50'
''
'8,0'
''
Пустых строк две: одна настоящая, из середины файла, вторая — хвост после последнего \n. Обе дадут ValueError с пустыми кавычками в конце сообщения. Лечится это не try/except, а фильтром: не парсить то, что после чистки осталось пустым.
def to_float(raw: str) -> float:
cleaned = raw.replace("\ufeff", "").replace("\u00a0", "").replace(" ", "")
return float(cleaned.replace(",", "."))
lines = ["77,59", "", "8,0", "1\u00a0234,50"]
prices = [to_float(x) for x in lines if x.strip()]
print(prices)
print(sum(prices))
[77.59, 8.0, 1234.5]
1320.09
Почему ошибка появляется только на первой строке CSV-файла?
Почти всегда виноват BOM — три служебных байта в начале файла, которые Excel пишет при сохранении в формате «CSV UTF-8». При чтении с encoding="utf-8" эти байты превращаются в невидимый символ \ufeff, и он прилипает к самому первому значению самого первого ряда. Все остальные ряды разбираются нормально — отсюда и ощущение, что «падает только первая строка», хотя данные в ней выглядят точно так же, как во второй.
row = "\ufeff77,59"
print(len(row))
print(repr(row))
Симптом «CSV из Excel не парсится в число, причём только первая строка» почти всегда упирается именно в это. Убрать BOM из первой ячейки CSV правильнее не вручную, а кодировкой при открытии файла: кодек utf-8-sig съедает его сам.
from pathlib import Path
Path("prices.csv").write_bytes("товар;цена\nкофе;77,59\n".encode("utf-8-sig"))
with open("prices.csv", encoding="utf-8") as f:
print(repr(f.readline()))
with open("prices.csv", encoding="utf-8-sig") as f:
print(repr(f.readline()))
'\ufeffтовар;цена\n'
'товар;цена\n'
Если файл вместо BOM отдаёт кракозябры или падает с UnicodeDecodeError, дело в другой кодировке — это разобрано отдельно в статье про cp1251 и UTF-8. А разбор CSV по-взрослому, через csv.reader, лежит в справочнике по CSV.
Чем эта ошибка отличается от invalid literal for int()?
Обе ошибки — это ValueError, но выдают их разные функции, и различие практическое. float() жалуется текстом could not convert string to float, int() — текстом invalid literal for int() with base 10. Главное отличие: int() строже и не принимает даже корректно записанное дробное число, потому что не знает, что делать с дробной частью — округлить, отбросить или ругаться.
print(int("12.5"))
ValueError: invalid literal for int() with base 10: '12.5'
Путь для строки с дробной частью — сначала float(), потом int():
print(float("12.5"))
print(int(float("12.5")))
| Вызов |
Результат |
float("12.5") |
12.5 |
int("12.5") |
ValueError: invalid literal for int() |
int(float("12.5")) |
12 |
float("12") |
12.0 |
float("") |
ValueError: could not convert string to float |
То есть по тексту ошибки ты сразу знаешь, какая функция упала: «float» в сообщении — упал float(), «int() with base 10» — упал int().
Проверять заранее или ловить исключение
Предварительная проверка через isdigit() не работает: "12.5".isdigit() — это False, потому что точка не цифра, и "77,59".isdigit() тоже False. Такая проверка отбросит все корректные дробные числа. Писать собственную регулярку под «число с запятой, пробелами и BOM» — тоже занятие на полдня. Надёжнее сначала почистить строку, а потом попробовать преобразовать и поймать ValueError — это и есть питоновский стиль EAFP.
def parse_price(raw: str):
cleaned = raw.replace("\ufeff", "").replace("\u00a0", "").replace(" ", "")
try:
return float(cleaned.replace(",", "."))
except ValueError:
return None
rows = ["77,59", "1\u00a0234,50", "", "нет данных", "\ufeff8,0"]
for row in rows:
print(repr(row), "->", parse_price(row))
'77,59' -> 77.59
'1\xa0234,50' -> 1234.5
'' -> None
'нет данных' -> None
'\ufeff8,0' -> 8.0
Такой парсер не падает на грязных данных и честно помечает непарсящиеся ячейки как None, а не подставляет вместо них ноль — иначе битая строка тихо исказит итоговую сумму. Разница между «спросить заранее» и «попробовать и поймать» разобрана в справочнике, EAFP и LBYL.
Модуль locale и Decimal: что из этого стоит трогать
В интернете часто советуют locale.atof() — функцию, которая разбирает число по правилам локали и потому понимает запятую. Совет рабочий ровно до момента, когда код уезжает на сервер: locale.setlocale(locale.LC_NUMERIC, "ru_RU.UTF-8") требует, чтобы эта локаль была установлена в системе, а в типичном Docker-контейнере её нет. К тому же локаль глобальна для всего процесса и меняет поведение постороннего кода.
import locale
try:
locale.setlocale(locale.LC_NUMERIC, "ru_RU.UTF-8")
print(locale.atof("77,59"))
except locale.Error:
print("локаль ru_RU.UTF-8 в системе не установлена")
Практическое правило: для разбора данных бери явную чистку строки, а locale оставь для отображения чисел пользователю. Отдельно про деньги: float — двоичная дробь, и точных десятичных копеек в нём нет. Если считаешь суммы, бери Decimal, которому строку тоже надо отдать с точкой.
from decimal import Decimal
print(0.1 + 0.2)
print(Decimal("77,59".replace(",", ".")) * 3)
Частые ошибки
- Замена запятой в английском формате. В строке
"1,234.50" запятая — разделитель разрядов, и replace(",", ".") превратит её в "1.234.50", что не парсится вообще. Сначала посмотри на формат глазами через repr(), потом решай, что менять.
- Голый
except:. Он проглотит и опечатку в имени переменной, и KeyboardInterrupt. Лови конкретно except ValueError.
- Забыть про
None. float(None) даёт TypeError, а не ValueError, и блок except ValueError его пропустит. Если в данных бывает None, пиши except (TypeError, ValueError).
- Проверка через
isdigit(). Она отбрасывает корректные дробные числа: "12.5".isdigit() возвращает False.
- Чистить BOM в каждой строке. BOM приходит из файла один раз, и лечится он в
open() через encoding="utf-8-sig", а не циклом lstrip("\ufeff") по всем ячейкам.
- Подставлять ноль вместо непарсящегося значения. Ноль неотличим от настоящей нулевой цены и молча ломает суммы и средние. Возвращай
None и считай такие строки отдельно.
- Не замечать
nan. float("nan") не падает, потому что nan — валидное значение. Строка "NaN" из выгрузки тихо станет числом, которое не равно даже самому себе: nan == nan даёт False, а nan != nan — наоборот, True. Сравнения <, >, <=, >= с nan тоже все False, поэтому фильтры и сортировки по такой колонке молча врут.
Практика
Теория закрепляется, когда парсер запускается. Три задачи из бесплатного каталога Python Arena — код проверяется скрытыми тестами прямо в браузере, ставить ничего не надо:
- Числа среди слов — вытащить числа из текста и сложить их: ровно та задача, где встречаются и мусорные токены, и пустые куски.
- Аккуратные пробелы — нормализация пробелов, шаг, который спасает от
\xa0 и двойных разделителей.
- Итог корзины со скидкой — арифметика по ценам, когда числа уже разобраны.
Мини-резюме
could not convert string to float — это ValueError от float(), и в кавычках сообщения лежит сама виноватая строка.
- В русских данных причина номер один — десятичная запятая:
float("77,59") падает, float("77.59") работает.
- Минимальный фикс —
replace(",", "."); для выгрузок нужна ещё чистка \xa0, обычных пробелов и BOM.
- Перенос строки
\n сам по себе безопасен: float("12.5\n") работает. Ломает пустая строка '', которая остаётся после разбиения текста.
- Падение только на первом ряду CSV — почти всегда BOM; лечится
encoding="utf-8-sig" в open().
int("12.5") даёт другую ошибку — invalid literal for int() with base 10; правильный путь int(float("12.5")).
- Для грязных данных надёжнее чистка плюс
try/except ValueError с возвратом None, а не предварительная проверка через isdigit().
Ошибка
could not convert string to floatзначит одно: вfloat()попала строка, которую Python не считает записью числа. В русских данных причина почти всегда одна и та же — десятичная запятая:float("77.59")возвращает77.59, аfloat("77,59")падает, потому что разделителем дробной части Python признаёт только точку.Если ты пришёл по запросу «could not convert string to float python запятая» — да, минимальный фикс это
price.replace(",", "."). Но одной замены хватает не всегда. Дальше по частоте идут пустая строка из хвоста файла, невидимый BOM в первой ячейке выгрузки из Excel и неразрывный пробел в разрядах вроде1 234,50. Разберём по одной причине за раз — от того, откуда строка пришла, а не от абстрактного правила.Что означает ошибка could not convert string to float?
Это
ValueError: функцияfloat()получила строку и не смогла разобрать её как число. Python принимает только строгий формат — необязательный знак, цифры, точка как разделитель дробной части и, при желании, экспонента вроде1e3. Всё остальное — запятая, значок валюты, буквы, пустота — приводит к исключению. Самое полезное в сообщении то, что оно печатает саму проблемную строку в кавычках: это готовая улика, по которой причина определяется за секунду.price = "77,59" print(float(price))Последняя строка вывода:
Читай то, что стоит в кавычках после двоеточия:
'77,59'— запятая вместо точки;''— пришла пустая строка;'\ufeff77,59'— в начале BOM из файла;'1\xa0234,50'— неразрывный пробел между разрядами;'12.5 руб.'— в строку затесался текст.Если ты видишь в кавычках
\ufeffили\xa0— символ невидимый, глазами его в терминале не найти. Поэтому первое действие при непонятной строке всегда одно:print(repr(raw)).Чтобы эту строку прочитать, сообщение должно дожить до твоих глаз. При запуске двойным кликом консольное окно гаснет вместе с процессом, и
ValueErrorисчезает вместе с ним — тогда сначала запусти файл из cmd, как разобрано в статье скрипт закрывается сразу после запуска.Почему float('12,5') выдаёт ошибку, а float('12.5') работает?
Потому что
float()разбирает строку по правилам синтаксиса самого Python, а не по правилам твоей локали. В исходном коде дробная часть отделяется точкой —x = 12.5, — и функция использует ровно тот же парсер. Запятая для него не разделитель, а посторонний символ, из-за которого вся строка перестаёт быть числом. Запись77,59приходит из Excel, 1С, банковских выгрузок и просто изinput(), поэтому именно она даёт эту ошибку чаще всего.float(...)"77.59"77.59"77,59"ValueError"1e3"1000.0"1_000.5"1000.5" 12.5\n"12.5""ValueError"nan"nanКороче говоря,
floatне принимает число с запятой ни в Python 3.12, ни в любой другой версии. Чтобы преобразовать строку с запятой в число, достаточно поменять разделитель:price = "77,59" print(float(price.replace(",", "."))) # 77.59Какие вообще строки Python считает числами и чем
float()отличается отint()— коротко разобрано в справочнике, в разделе про преобразование типов.Когда одной замены replace(',', '.') уже мало
Замена лечит запятую и больше ничего. Настоящая выгрузка приносит ещё и разряды, причём разделитель разрядов бывает двух видов: обычный пробел и неразрывный пробел
\xa0(U+00A0), который Excel и веб-страницы вставляют, чтобы число не разорвалось при переносе строки. На экране они выглядят абсолютно одинаково.raw = " 1\u00a0234,50\n" print(repr(raw))Тонкость:
float()сам отбрасывает пробельные символы по краям строки, включая неразрывный, но не трогает те, что стоят внутри числа. Значит, чистить надо весь набор сразу:def to_float(raw: str) -> float: cleaned = raw.replace("\ufeff", "").replace("\u00a0", "").replace(" ", "") return float(cleaned.replace(",", ".")) print(to_float("77,59")) print(to_float(" 1\u00a0234,50\n")) print(to_float("12.5"))replace(",", ".")"77,59"77.5977.59"1 234,50"(обычный пробел)ValueError1234.5"1\xa0234,50"(неразрывный)ValueError1234.5"\ufeff8,0"(BOM)ValueError8.0Обрати внимание на третью строку вывода:
to_float("12.5")тоже работает. Замена запятой на точку безопасна для строк, где запятой нет, поэтому одну функцию можно применять ко всей колонке, не разделяя данные на «русские» и «английские».Соблазн почистить строку через
strip()илиrstrip()тут велик, ноrstrip()— отдельная ловушка: он снимает набор символов, а не окончание строки. Как это молча съедает лишние буквы, разобрано в статье про удаление расширения файла.Пустая строка и перенос: что из них правда ломает float()
Народное объяснение «мешает
\nв конце» неверно.float()сам отбрасывает пробельные символы по краям, поэтомуfloat("12.5\n")спокойно вернёт12.5. Ломает не перенос, а пустая строка, которая из этого переноса рождается: если файл заканчивается символом\n, то при разбиении по строкам последним элементом окажется''.text = "77,59\n1 234,50\n\n8,0\n" for line in text.split("\n"): print(repr(line))Пустых строк две: одна настоящая, из середины файла, вторая — хвост после последнего
\n. Обе дадутValueErrorс пустыми кавычками в конце сообщения. Лечится это не try/except, а фильтром: не парсить то, что после чистки осталось пустым.def to_float(raw: str) -> float: cleaned = raw.replace("\ufeff", "").replace("\u00a0", "").replace(" ", "") return float(cleaned.replace(",", ".")) lines = ["77,59", "", "8,0", "1\u00a0234,50"] prices = [to_float(x) for x in lines if x.strip()] print(prices) print(sum(prices))Почему ошибка появляется только на первой строке CSV-файла?
Почти всегда виноват BOM — три служебных байта в начале файла, которые Excel пишет при сохранении в формате «CSV UTF-8». При чтении с
encoding="utf-8"эти байты превращаются в невидимый символ\ufeff, и он прилипает к самому первому значению самого первого ряда. Все остальные ряды разбираются нормально — отсюда и ощущение, что «падает только первая строка», хотя данные в ней выглядят точно так же, как во второй.row = "\ufeff77,59" print(len(row)) # 6, хотя на экране видно пять символов print(repr(row)) # '\ufeff77,59'Симптом «CSV из Excel не парсится в число, причём только первая строка» почти всегда упирается именно в это. Убрать BOM из первой ячейки CSV правильнее не вручную, а кодировкой при открытии файла: кодек
utf-8-sigсъедает его сам.from pathlib import Path Path("prices.csv").write_bytes("товар;цена\nкофе;77,59\n".encode("utf-8-sig")) with open("prices.csv", encoding="utf-8") as f: print(repr(f.readline())) with open("prices.csv", encoding="utf-8-sig") as f: print(repr(f.readline()))Если файл вместо BOM отдаёт кракозябры или падает с
UnicodeDecodeError, дело в другой кодировке — это разобрано отдельно в статье про cp1251 и UTF-8. А разбор CSV по-взрослому, черезcsv.reader, лежит в справочнике по CSV.Чем эта ошибка отличается от invalid literal for int()?
Обе ошибки — это
ValueError, но выдают их разные функции, и различие практическое.float()жалуется текстомcould not convert string to float,int()— текстомinvalid literal for int() with base 10. Главное отличие:int()строже и не принимает даже корректно записанное дробное число, потому что не знает, что делать с дробной частью — округлить, отбросить или ругаться.print(int("12.5"))Путь для строки с дробной частью — сначала
float(), потомint():print(float("12.5")) # 12.5 print(int(float("12.5"))) # 12float("12.5")12.5int("12.5")ValueError: invalid literal for int()int(float("12.5"))12float("12")12.0float("")ValueError: could not convert string to floatТо есть по тексту ошибки ты сразу знаешь, какая функция упала: «float» в сообщении — упал
float(), «int() with base 10» — упалint().Проверять заранее или ловить исключение
Предварительная проверка через
isdigit()не работает:"12.5".isdigit()— этоFalse, потому что точка не цифра, и"77,59".isdigit()тожеFalse. Такая проверка отбросит все корректные дробные числа. Писать собственную регулярку под «число с запятой, пробелами и BOM» — тоже занятие на полдня. Надёжнее сначала почистить строку, а потом попробовать преобразовать и пойматьValueError— это и есть питоновский стиль EAFP.def parse_price(raw: str): cleaned = raw.replace("\ufeff", "").replace("\u00a0", "").replace(" ", "") try: return float(cleaned.replace(",", ".")) except ValueError: return None rows = ["77,59", "1\u00a0234,50", "", "нет данных", "\ufeff8,0"] for row in rows: print(repr(row), "->", parse_price(row))Такой парсер не падает на грязных данных и честно помечает непарсящиеся ячейки как
None, а не подставляет вместо них ноль — иначе битая строка тихо исказит итоговую сумму. Разница между «спросить заранее» и «попробовать и поймать» разобрана в справочнике, EAFP и LBYL.Модуль locale и Decimal: что из этого стоит трогать
В интернете часто советуют
locale.atof()— функцию, которая разбирает число по правилам локали и потому понимает запятую. Совет рабочий ровно до момента, когда код уезжает на сервер:locale.setlocale(locale.LC_NUMERIC, "ru_RU.UTF-8")требует, чтобы эта локаль была установлена в системе, а в типичном Docker-контейнере её нет. К тому же локаль глобальна для всего процесса и меняет поведение постороннего кода.import locale try: locale.setlocale(locale.LC_NUMERIC, "ru_RU.UTF-8") print(locale.atof("77,59")) except locale.Error: print("локаль ru_RU.UTF-8 в системе не установлена")Практическое правило: для разбора данных бери явную чистку строки, а
localeоставь для отображения чисел пользователю. Отдельно про деньги:float— двоичная дробь, и точных десятичных копеек в нём нет. Если считаешь суммы, бериDecimal, которому строку тоже надо отдать с точкой.from decimal import Decimal print(0.1 + 0.2) # 0.30000000000000004 print(Decimal("77,59".replace(",", ".")) * 3) # 232.77Частые ошибки
"1,234.50"запятая — разделитель разрядов, иreplace(",", ".")превратит её в"1.234.50", что не парсится вообще. Сначала посмотри на формат глазами черезrepr(), потом решай, что менять.except:. Он проглотит и опечатку в имени переменной, иKeyboardInterrupt. Лови конкретноexcept ValueError.None.float(None)даётTypeError, а неValueError, и блокexcept ValueErrorего пропустит. Если в данных бываетNone, пишиexcept (TypeError, ValueError).isdigit(). Она отбрасывает корректные дробные числа:"12.5".isdigit()возвращаетFalse.open()черезencoding="utf-8-sig", а не цикломlstrip("\ufeff")по всем ячейкам.Noneи считай такие строки отдельно.nan.float("nan")не падает, потому чтоnan— валидное значение. Строка"NaN"из выгрузки тихо станет числом, которое не равно даже самому себе:nan == nanдаётFalse, аnan != nan— наоборот,True. Сравнения<,>,<=,>=сnanтоже всеFalse, поэтому фильтры и сортировки по такой колонке молча врут.Практика
Теория закрепляется, когда парсер запускается. Три задачи из бесплатного каталога Python Arena — код проверяется скрытыми тестами прямо в браузере, ставить ничего не надо:
\xa0и двойных разделителей.Мини-резюме
could not convert string to float— этоValueErrorотfloat(), и в кавычках сообщения лежит сама виноватая строка.float("77,59")падает,float("77.59")работает.replace(",", "."); для выгрузок нужна ещё чистка\xa0, обычных пробелов и BOM.\nсам по себе безопасен:float("12.5\n")работает. Ломает пустая строка'', которая остаётся после разбиения текста.encoding="utf-8-sig"вopen().int("12.5")даёт другую ошибку —invalid literal for int() with base 10; правильный путьint(float("12.5")).try/except ValueErrorс возвратомNone, а не предварительная проверка черезisdigit().