Быстро удалить спецификацию UTF-8

PHP Linux

На работе более или менее вы столкнетесь со спецификацией UTF-8 (далее BOM), а иногда сторонние инструменты не поддерживают ее, и вам нужно удалить спецификацию самостоятельно, например, файл SQL, экспортированный Alibaba. В Cloud есть спецификация, но Navicat ее не поддерживает. Чтобы удалить спецификацию.

Тестовый файл, используемый позже, представляет собой файл SQL, экспортированный Alibaba Cloud, 265M, и файл был кэширован во время теста (входные данные файловой системы, отображаемые по времени, близки к 0)

Перейти к спецификации с помощью sed

sed -e '1s/^\xef\xbb\xbf//' file

Взгляните на время, затрачиваемое методом sed со временем:

$ /usr/bin/time -v sed -e '1s/^\xef\xbb\xbf//' sqlResult_1601835.sql > /dev/null
        ...
        User time (seconds): 0.33
        System time (seconds): 0.11
        Percent of CPU this job got: 98%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.46
        ...

Пользовательское время велико, потому что sed будет обрабатывать каждую строку, но на самом деле только первая строка имеет спецификацию, поэтому ЦП тратится впустую.

sed также поддерживает обновления на месте (-i):

$ /usr/bin/time -v sed -e '1s/^\xef\xbb\xbf//' sqlResult_1601835.sql -i
        ...
        User time (seconds): 1.31
        System time (seconds): 3.89
        Percent of CPU this job got: 71%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:07.32
        ...

Поскольку файл записывается, он будет работать медленнее, с помощью strace можно найти, отправить через вывод во временный файл и перезаписать исходный файл, реализованное обновление.

open("sqlResult_1601835.sql", O_RDONLY) = 3
open("./sedGlXm60", O_RDWR|O_CREAT|O_EXCL, 0600) = 4
...
rename("./sedGlXm60", "sqlResult_1601835.sql")

Используйте хвост, чтобы перейти к спецификации

tail --bytes=+4 file

С хвостом вы можете напрямую пропустить спецификацию, а затем напрямую скопировать содержимое файла, уменьшив ненужную обработку ЦП:

$ /usr/bin/time -v tail --bytes=+4 sqlResult_1601835.sql > /dev/null
        ...
        User time (seconds): 0.01
        System time (seconds): 0.12
        Percent of CPU this job got: 96%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.14
        ...

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

strip-bom

Чтобы объединить преимущества sed и tail, я написалstrip-bom, который поддерживает обновление файлов на месте.

Сначала проверьте перенаправление:

$ /usr/bin/time -v php strip-bom.phar sqlResult_1601835.sql > /dev/null
        ...
        User time (seconds): 0.11
        System time (seconds): 0.22
        Percent of CPU this job got: 98%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.35
        ...

Только на 20% быстрее, чем sed, время пользователя меньше, но системное время увеличено. Поскольку это циклический процесс чтения и записи, каждый цикл представляет собой вызов чтения и записи, поэтому я добавил параметр для настройки размера блока каждого чтения, что может уменьшить количество циклов и системных вызовов и может быть на 60% быстрее. чем сед:

$ /usr/bin/time -v php strip-bom.phar -b 16384 sqlResult_1601835.sql > /dev/null
        ...
        User time (seconds): 0.06
        System time (seconds): 0.12
        Percent of CPU this job got: 96%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.19

Протестируйте обновление на месте, на 30% быстрее, чем sed:

$ /usr/bin/time -v php strip-bom.phar -i -b 16384 sqlResult_1601835.sql
        User time (seconds): 0.23
        System time (seconds): 0.67
        Percent of CPU this job got: 17%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:05.11

copy_file_range

В Linux 4.5 добавлен системный вызов:

ssize_t copy_file_range(int fd_in, loff_t *off_in,
                               int fd_out, loff_t *off_out,
                               size_t len, unsigned int flags);

Вы можете напрямую копировать содержимое между двумя файловыми дескрипторами и обычно только одним системным вызовом, поэтому вы можете обратиться к sed для копирования во временный файл, а затем перезаписать старый файл, код реализации:Gist

тестовое задание:

$ /usr/bin/time -v ./copy_file_range sqlResult_1601835.sql
        ...
        User time (seconds): 0.00
        System time (seconds): 2.47
        Percent of CPU this job got: 37%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:06.52

Сокращение системных вызовов лишь немного быстрее, чем sed, а копирование во временные файлы по-прежнему медленнее, чем обновления на месте.

dos2unix перейти к спецификации

Я всегда думал, что dos2unix был преобразован в CRLF.После прочтения комментария Feng_Yu, я прочитал справочную страницу.Оказывается, что dos2unix имеет много функций, в том числе возможность перейти в BOM (-r):

$ /usr/bin/time -v dos2unix -r sqlResult_1601835.sql
dos2unix: 正在转换文件 sqlResult_1601835.sql 为Unix格式...
        Command being timed: "dos2unix -r sqlResult_1601835.sql"
        User time (seconds): 10.01
        System time (seconds): 0.90
        Percent of CPU this job got: 60%
        Elapsed (wall clock) time (h:mm:ss or m:ss): 0:18.20

Реализация dos2unix похожа на sed, и он тоже записывается во временный файл и потом перезаписывается.Как и sed, он будет обрабатывать каждую строку, так что производительность не очень хорошая.