У меня есть несколько текстовых файлов с разделителями табуляции, полученных с сайта FFIEC (https://cdr.ffiec.gov/public/PWS/DownloadBulkData.aspx) <Отчеты о вызовах — один период, расписание RIE>, которые имеют LF (строка подача) символы без символов CR (возврат каретки) в одном или нескольких полях. Файлы загружаются в SQL Server 2022 (или используются в Excel). Каждая запись (строка) файла заканчивается последовательностью CRLF. Проблема в том, что LF в поле интерпретируется как начало следующей записи при чтении текстового файла (в Excel или с использованием SSIS для импорта в SQL Server).
Я знаю о \r\n в Windows по сравнению с \n UNIX/Linux и подозреваю, что Python обрабатывает любой из них как последовательность. Я не пробовал кодировку Latin-1 или cp1252.
Я использую Windows 11 Pro. Сценарий вызывается из команды оболочки (хранимой процедуры SQL или Excel VBA) и является частью более крупной группы сценариев очистки файлов для импорта.
Моя попытка решения состояла в том, чтобы прочитать файл, перебрать его по одному символу за раз, найти LF '\n', которому не предшествует CR '\r', и заменить его точкой с запятой ';'.
Код Python (v3.12):
import sys
def stripLFwoCR_file(file_path):
# Read the entire file contents
with open(file_path, 'r', encoding='utf-8') as file:
input_data = file.read()
# Initialize output
output_data = []
# Iterate input content 1 character at a time
# Replace line feed characters '\n' not preceded by carriage return characters '\r' with ';'
i = 0
while i < len(input_data):
if input_data[i] == '\n':
# If previous character is not '\r' then replace '\n' with ';'
if i == 0 or input_data[i-1] != '\r':
output_data.append(';')
# Skip this '\n'
else:
output_data.append(input_data[i])
i += 1
# Write the modified content back to the file, overwriting it
with open(file_path, 'w', encoding='utf-8') as file:
file.write(''.join(output_data))
if __name__ == "__main__":
args = sys.argv
# args[0] = current file
# args[1] = function name
# args[2:] = function args : (*unpacked)
globals()[args[1]](*args[2:])
Проблема заключается в том, что сценарий заменяет все LF И все CRLF в файле на ';'.
Образец оригинала документа (LF без CR) Строки 10–14 являются частью одной записи. Строки 16–21 — это одна запись.
Обновление: мне нужно прочитать руководство! Начиная с версии 3.x, в Python есть возможность игнорировать или использовать другую автозамену для новой строки. Мой исходный код также содержит логическую ошибку в цикле while.
В итоге я использовал это, потому что это требовало меньше переписывания остальной части моего кода. Я проверил ответ @JRiggles и отметил его как решение (более чистое, меньше кода):
import sys
def stripLFwoCR_file(file_path):
# Read the entire file contents
with open(file_path, 'r', encoding='utf-8', newline='\r\n') as file:
input_data = file.read()
# Initialize output
output_data = []
# Iterate input content 1 character at a time
# Replace line feed characters '\n' not preceded by carriage return characters '\r' with ';'
i = 0
while i < len(input_data):
if input_data[i] == '\n':
# If previous character is not '\r' then replace '\n' with ';'
if i == 0 or input_data[i-1] != '\r':
# Skip this '\n' and replace
output_data.append(';')
else:
output_data.append(input_data[i])
else:
output_data.append(input_data[i])
i += 1
# Write the modified content back to the file, overwriting it
with open(file_path, 'w', encoding='utf-8', newline='\n') as file:
file.write(''.join(output_data))
if __name__ == "__main__":
args = sys.argv
# args[0] = current file
# args[1] = function name
# args[2:] = function args : (*unpacked)
globals()[args[1]](*args[2:])
🤔 А знаете ли вы, что...
Python - это универсальный язык программирования.
Звучит как работа для re.sub
. Шаблон (?<!\r)\n
будет соответствовать любым символам LF \n
, которым не предшествует возврат каретки (CR) \r
.
Вот пример файла: sample data.txt
(скриншот, показывающий окончания строк)
Чтобы избежать преобразований окончания строк, откройте файл в режиме двоичного чтения 'rb'
import re
pattern = b'(?<!\r)\n' # match any \n not preceded by \r
with open(r'<path to>\sample data.txt', 'rb') as file:
data = file.read()
print('Pre-substitution: ', data)
# replace any matches with a semicolon ';'
result = re.sub(pattern, b';', data)
print('Post-substitution: ', result)
Это печатает:
Pre-substitution: b'this line ends with CRLF\r\nthis line ends with LF\nthis line ends with CRLF\r\nthis line ends with LF\nthis line ends with CRLF\r\n'
Post-substitution: b'this line ends with CRLF\r\nthis line ends with LF;this line ends with CRLF\r\nthis line ends with LF;this line ends with CRLF\r\n'
Стоит отметить, что все последовательные \n
будут заменены, поэтому \n\n\n
становится ;;;
, а \r\n\n
становится r\n;
.
Также обратите внимание, что строка pattern
и значение подстановки являются байтовыми строками (b'<str>'
) — если вы этого не сделаете, вы получите TypeError
!