Это классическая точка болида unicode python! Рассмотрим следующее:
a = u'bats\u00E0'
print a
=> batsà
Все хорошо до сих пор, но если мы назовем str (a), посмотрим, что получится:
str(a)
Traceback (most recent call last):
File "", line 1, in
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe0' in position 4: ordinal not in range(128)
О, падайте, это не собирается любой хороший! Чтобы исправить ошибку, явным образом закодируйте байты с помощью .encode и скажите python, какой кодек использовать:
a.encode('utf-8')
=> 'bats\xc3\xa0'
print a.encode('utf-8')
=> batsà
Voil \ u00E0!
Проблема в том, что при вызове str ( ), python использует кодировку символов по умолчанию, чтобы попытаться и закодировать байты, которые вы ему дали, что в вашем случае иногда представляет символы юникода. Чтобы устранить проблему, вы должны сказать python, как обращаться со строкой, которую вы даете ей, используя .encode ('whatever_unicode').
Для отличной экспозиции по этой теме см. Раздел PyCon от Ned Batchelder: http://nedbatchelder.com/text/unipain .html
Вы не должны использовать ls
таким образом, и цикл for
также не подходит. Кроме того, каталог назначения должен находиться за пределами исходного каталога.
mkdir /path/to/destination
find . -type f -exec iconv -f iso-8859-1 -t utf-8 "{}" -o /path/to/destination/"{}" \;
Нет необходимости в петле. Опция -type f
включает файлы и исключает каталоги.
Редактировать:
Версия iconv
для OS X не имеет опции -o
. Попробуйте это:
find . -type f -exec bash -c 'iconv -f iso-8859-1 -t utf-8 "{}" > /path/to/destination/"{}"' \;
Некоторые хорошие ответы, но я обнаружил, что это намного проще в моем случае с вложенным каталогом из сотен файлов для конвертации:
$ vim $(find . -type f)
# in vim, go into command mode (:)
:set nomore
:bufdo set fileencoding=utf8 | w
Для рекурсивного преобразования полного дерева каталогов из iso-8859-1 в utf-8, включая создание подкаталогов, ни одно из приведенных выше кратких решений не сработало для меня, поскольку структура каталога не была создана в целевом объекте. Основываясь на ответе Денниса Уильямсона, я нашел следующее решение:
find . -type f -exec bash -c 't="/tmp/dest"; mkdir -p "$t/`dirname {}`"; iconv -f iso-8859-1 -t utf-8 "{}" > "$t/{}"' \;
Он создаст клон текущего поддерева каталога в /tmp/dest
(с учетом ваших потребностей), включая все подкаталоги и все iso-8859-1
файлы преобразованы в utf-8
. Проверено на macosx.
Кстати: проверьте кодировки файлов с помощью:
file -I file.php
, чтобы получить информацию о кодировке.
Надеюсь, это поможет.
Конвертирует все файлы с расширением имени файла .php
- в текущем каталоге и его подкаталогах - сохраняя структуру каталога:
find . -name "*.php" -exec sh -c "iconv -f ISO-8859-1 -t UTF-8 {} > {}.utf8" \; -exec mv "{}".utf8 "{}" \;
Примечания:
Чтобы получить список Для файлов, которые будут предварительно нацелены, просто запустите команду без флагов -exec
(например: find . -name "*.php"
). Создание резервной копии - хорошая идея.
Подобное использование sh
позволяет передавать и перенаправлять с -exec, что необходимо, поскольку не все версии iconv поддерживают флаг -o
.
Добавление .utf8
к имени файла вывода и последующее удаление может показаться странным, но это необходимо. Использование одного и того же имени для выходных и входных файлов может вызвать следующие проблемы:
Для больших файлов (по моему опыту около 30 КБ) это вызывает дамп ядра (или termination by signal 7
)
Некоторые версии iconv создают выходной файл до того, как прочитают входной файл, что означает, что если входные и выходные файлы имеют одинаковые имена, входной файл перезаписывается пустым файл до его прочтения.
Я создаю следующий сценарий, который (i) создает резервные копии всех текстовых файлов в каталоге, «преобразованных», (ii) проверяет кодировку каждого текстового файла и (iii) преобразует в UTF-8 только текстовые файлы в ISO Кодирование 8859-1.
FILES=*.tex
for f in $FILES
do
filename="${f%.*}"
echo -n "$f"
#file -I $f
if file -I $f | grep -wq "iso-8859-1"
then
mkdir -p converted
cp $f ./converted
iconv -f ISO-8859-1 -t UTF-8 $f > "${filename}_utf8.tex"
mv "${filename}_utf8.tex" $f
echo ": CONVERTED TO UTF-8."
else
echo ": UTF-8 ALREADY."
fi
done
Все хорошо с приведенными выше ответами, но если это «смешанный» проект, то есть уже есть файлы UTF8, то у нас могут возникнуть проблемы, поэтому вот мое решение, я сначала проверяю кодировку файлов.
#!/bin/bash
# file name: to_utf8
# current encoding:
encoding=$(file -i "$1" | sed "s/.*charset=\(.*\)$/\1/")
if [ "${encoding}" = "iso-8859-1" ] || [ "${encoding}" = "iso-8859-2" ];
then
echo "recoding from ${encoding} to UTF-8 file : $1"
recode ISO-8859-2..UTF-8 "$1"
fi
#example:
#find . -name "*.php" -exec to_utf8 {} \;
Используйте mkdir -p "${a%/*}";
перед иконкой.
Обратите внимание, что вы используете потенциально опасную конструкцию for
, когда в именах файлов есть пробелы, см. http://porkmail.org/era/unix/award.html .
На unix.stackexchange.com был задан похожий вопрос, и пользователь manatwork предложил перекодировать, что очень хорошо помогает.
Я использую его для преобразования ucs-2 в utf-8 вместо
recode ucs-2..utf-8 *.txt
Если все файлы, которые вы должны конвертировать, являются .php, вы можете использовать следующее, которое по умолчанию является рекурсивным:
for a in $(find . -name "*.php"); do iconv -f iso-8859-1 -t utf-8 <"$a" >new/"$a" ; done
Я полагаю, что ваши ошибки были связаны с тем, что ls -R
также производит вывод, который не может быть распознан iconv как правильное имя файла, что-то вроде ./my/dir/structure:
Используя ответы Денниса Уильямсона и Альберто Закканьи, я придумал следующий сценарий, который преобразует все файлы указанного типа из всех подкаталогов. Затем выходные данные собираются в одну папку, которая задается как /path/to/destination
mkdir /path/to/destination
for a in $(find . -name "*.php");
do
filename=$(basename $a);
echo $filename
iconv -f iso-8859-1 -t utf-8 <"$a" >"/path/to/destination/$filename";
done
. Базовое имя функции возвращает имя файла без пути к файлу.
Альтернатива (интерактивная): Теперь я также создал интерактивный сценарий пользователя, который позволяет вам решить, хотите ли вы перезаписать старые файлы или просто переименовать их. Дополнительная благодарность идет в tbsalling
for a in $(find . -name "*.tex");
do
iconv -f iso-8859-1 -t utf-8 <"$a" >"$a".utf8 ;
done
echo "Should the original files be replaced (Y/N)?"
read replace
if [ "$replace" == "Y" ]; then
echo "Original files have been replaced."
for a in $(find . -name "*.tex.utf8");
do
file_no_suffix=$(basename -s .tex.utf8 "$a");
directory=$(dirname "$a");
mv "$a" "$directory"/"$file_no_suffix".tex;
done
else
echo "Original files have been converted and converted files were saved with suffix '.utf8'"
fi
Удачи с этим, и я был бы благодарен за любые комментарии, чтобы улучшить его, спасибо!
find . -iname "*.php" | xargs -I {} echo "iconv -f ISO-8859-1 -t UTF-8 \"{}\" > \"{}-utf8.php\""