将任何编码转换为UTF-8的图标v。

24
我正在尝试将iconv指向一个目录,所有文件都将被转换为UTF-8编码,而不考虑当前的编码。
我正在使用这个脚本,但你必须指定你从哪个编码进行转换。如何使它自动检测当前的编码?
dir_iconv.sh
#!/bin/bash

ICONVBIN='/usr/bin/iconv' # path to iconv binary

if [ $# -lt 3 ]
then
  echo "$0 dir from_charset to_charset"
  exit
fi

for f in $1/*
do
  if test -f $f
  then
    echo -e "\nConverting $f"
    /bin/mv $f $f.old
    $ICONVBIN -f $2 -t $3 $f.old > $f
  else
    echo -e "\nSkipping $f - not a regular file";
  fi
done

终端命令行
sudo convert/dir_iconv.sh convert/books CURRENT_ENCODING utf8

8个回答

21

也许您正在寻找 enca:

Enca是一种极其简单的字符集分析器。它可以检测文本文件的字符集和编码,并使用内置转换器或外部库和工具(如libiconv、librecode或cstocs)将它们转换为其他编码。

目前,它支持白俄罗斯语、保加利亚语、克罗地亚语、捷克语、爱沙尼亚语、匈牙利语、拉脱维亚语、立陶宛语、波兰语、俄语、斯洛伐克语、斯洛文尼亚语、乌克兰语、中文和一些多字节编码,独立于语言。

请注意,通常情况下自动检测当前编码是一个困难的过程(同一字节序列在多种编码中都可能是正确的文本)。enca基于您告诉它要检测的语言的启发式方法来减少编码的数量。您可以使用enconv将文本文件转换为单一编码


你的 Enca 链接无法使用。这是更新后的链接吗?http://freecode.com/projects/enca - trante
似乎Enca自那时起已经迁移到Github。请注意,freecode网站也链接到不存在的Gitorious链接。我已在答案中更新了链接。 - Michal Kottman
我想知道您是不是指的iconv而不是econv,因为在手册中找不到econv - Daniel Dropik
1
语法:enca -x utf8 -L mylanguage file.srt - kenorb
您的版本中有效的语言列表为:enca -l languages ... 但是在更新时,UBUNTU很丑陋,我的 enca --version 是2005!如何升级它? - Peter Krauss

13
您可以使用标准的GNU工具文件和awk获取所需内容。例如: file -bi .xsession-errors 给出结果: "text/plain; charset=us-ascii"
因此,file -bi .xsession-errors |awk -F "=" '{print $2}' 给出结果 "us-ascii"
我在脚本中这样使用它:
CHARSET="$(file -bi "$i"|awk -F "=" '{print $2}')"

if [ "$CHARSET" != utf-8 ]; then
  iconv -f "$CHARSET" -t utf8 "$i" -o outfile
fi

3
file 使用的启发式算法可能相当粗糙。小心。 - tripleee
这种方法对于其他编码的Unix文件效果很好,但要小心微软Windows文件可能是UTF-16LE编码。这个答案会将其转换为UTF-8,但会在文件开头留下一个不雅的BOM标记,并在每行末尾留下一个CR。对于我知道来自微软Windows的文件,我用dos2unix程序运气很好,它可以处理一切:检测编码、从UTF-16转换为UTF-8、移除BOM并改变行尾。 - undefined

5

编译它们全部。进入目录,创建dir2utf8.sh

#!/bin/bash
# converting all files in a dir to utf8

for f in *
do
  if test -f $f then
    echo -e "\nConverting $f"
    CHARSET="$(file -bi "$f"|awk -F "=" '{print $2}')"
    if [ "$CHARSET" != utf-8 ]; then
      iconv -f "$CHARSET" -t utf8 "$f" -o "$f"
    fi
  else
    echo -e "\nSkipping $f - it's a regular file";
  fi
done

5

这是使用 recodeuchardet 的方法,对所有文件进行就地转换的 解决方案

#!/bin/bash

apt-get -y install recode uchardet > /dev/null
find "$1" -type f | while read FFN # 'dir' should be changed...
do
  encoding=$(uchardet "$FFN")
  echo "$FFN: $encoding"
  enc=`echo $encoding | sed 's#^x-mac-#mac#'`
  set +x
  recode $enc..UTF-8 "$FFN"
done

将其放入convert-dir-to-utf8.sh中并运行:

bash convert-dir-to-utf8.sh /pat/to/my/trash/dir

请注意sed在这里是Mac编码的一种解决方法。许多不常见的编码需要像这样的解决方法。

uchardet 拯救了我的脚本。 - Éderson T. Szlachta
提示:备份您的文件并使用合并工具检查/比较更改。问题可能会出现! - Eduardo Lucio
1
recode 似乎已经不再维护,除了这个分支 - Pablo Bianchi

1

第一个答案

#!/bin/bash

find "<YOUR_FOLDER_PATH>" -name '*' -type f -exec grep -Iq . {} \; -print0 |
while IFS= read -r -d $'\0' LINE_FILE; do
  CHARSET=$(uchardet $LINE_FILE)
  echo "Converting ($CHARSET) $LINE_FILE"

  # NOTE: Convert/reconvert to utf8. By Questor
  iconv -f "$CHARSET" -t utf8 "$LINE_FILE" -o "$LINE_FILE"

  # NOTE: Remove "BOM" if exists as it is unnecessary. By Questor
  # [Refs.: https://dev59.com/enE95IYBdhLWcg3wn_f2#2223926 ,
  # https://dev59.com/CVcO5IYBdhLWcg3wxEgP#45240995 ]
  sed -i '1s/^\xEF\xBB\xBF//' "$LINE_FILE"

done
# [Refs.: https://justrocketscience.com/post/handle-encodings ,
# https://dev59.com/UGkw5IYBdhLWcg3w1eG7#9612232 ,
# https://dev59.com/w2445IYBdhLWcg3wnrvM#13659891 ]
更进一步的问题:我不确定我的方法是否最安全。我之所以这么说是因为我注意到有些文件没有正确转换(字符会丢失)或者被“截断”。我怀疑这与“iconv”工具或使用“uchardet”工具获取的字符集信息有关。我对@demofly提出的解决方案感到好奇,因为它可能更安全。

另一个答案

基于@demofly的答案:

#!/bin/bash

find "<YOUR_FOLDER_PATH>" -name '*' -type f -exec grep -Iq . {} \; -print0 |
while IFS= read -r -d $'\0' LINE_FILE; do
  CHARSET=$(uchardet $LINE_FILE)
  REENCSED=`echo $CHARSET | sed 's#^x-mac-#mac#'`
  echo "\"$CHARSET\" \"$LINE_FILE\""

  # NOTE: Convert/reconvert to utf8. By Questor
  recode $REENCSED..UTF-8 "$LINE_FILE" 2> STDERR_OP 1> STDOUT_OP

  STDERR_OP=$(cat STDERR_OP)
  rm -f STDERR_OP
  if [ -n "$STDERR_OP" ] ; then

    # NOTE: Convert/reconvert to utf8. By Questor
    iconv -f "$CHARSET" -t utf8 "$LINE_FILE" -o "$LINE_FILE" 2> STDERR_OP 1> STDOUT_OP

    STDERR_OP=$(cat STDERR_OP)
    rm -f STDERR_OP
  fi

  # NOTE: Remove "BOM" if exists as it is unnecessary. By Questor
  # [Refs.: https://dev59.com/enE95IYBdhLWcg3wn_f2#2223926 ,
  # https://dev59.com/CVcO5IYBdhLWcg3wxEgP#45240995 ]
  sed -i '1s/^\xEF\xBB\xBF//' "$LINE_FILE"

  if [ -n "$STDERR_OP" ] ; then
    echo "ERROR: \"$STDERR_OP\""
  fi
  STDOUT_OP=$(cat STDOUT_OP)
  rm -f STDOUT_OP
  if [ -n "$STDOUT_OP" ] ; then
    echo "RESULT: \"$STDOUT_OP\""
  fi
done
# [Refs.: https://justrocketscience.com/post/handle-encodings ,
# https://dev59.com/UGkw5IYBdhLWcg3w1eG7#9612232 ,
# https://dev59.com/w2445IYBdhLWcg3wnrvM#13659891 ]

第三个答案

使用recode和vim的混合方案:

#!/bin/bash

find "<YOUR_FOLDER_PATH>" -name '*' -type f -exec grep -Iq . {} \; -print0 |
while IFS= read -r -d $'\0' LINE_FILE; do
  CHARSET=$(uchardet $LINE_FILE)
  REENCSED=`echo $CHARSET | sed 's#^x-mac-#mac#'`
  echo "\"$CHARSET\" \"$LINE_FILE\""

  # NOTE: Convert/reconvert to utf8. By Questor
  recode $REENCSED..UTF-8 "$LINE_FILE" 2> STDERR_OP 1> STDOUT_OP

  STDERR_OP=$(cat STDERR_OP)
  rm -f STDERR_OP
  if [ -n "$STDERR_OP" ] ; then

    # NOTE: Convert/reconvert to utf8. By Questor
    bash -c "</dev/tty vim -u NONE +\"set binary | set noeol | set nobomb | set encoding=utf-8 | set fileencoding=utf-8 | wq\" \"$LINE_FILE\""

  else

    # NOTE: Remove "BOM" if exists as it is unnecessary. By Questor
    # [Refs.: https://dev59.com/enE95IYBdhLWcg3wn_f2#2223926 ,
    # https://dev59.com/CVcO5IYBdhLWcg3wxEgP#45240995 ]
    sed -i '1s/^\xEF\xBB\xBF//' "$LINE_FILE"

  fi
done

这是具有最高完美转换数量的解决方案。此外,我们没有任何被截断的文件。
  • 警告:备份您的文件并使用合并工具检查/比较更改。问题可能会出现!
  • 提示:在与合并工具进行初步比较后,可以执行命令sed -i '1s/^\xEF\xBB\xBF//' "$LINE_FILE" 进行转换,因为它可能会导致“差异”。
  • 注意:使用find进行搜索将从给定路径(“”)及其子文件夹中获取所有非二进制文件。

1
两个小提示:我会用"$1"替换<YOUR_FOLDER_PATH>,让最终用户传递文件夹路径。对于MacOS用户,您需要运行:brew install recode uchardet gnu-sed,然后将sed更改为gsed才能使其正常工作。使用grep -I删除二进制文件的工作做得很好。顶级! - phyatt
你的建议几乎完全被接受了。我没有采纳“我会用$1替换<YOUR_FOLDER_PATH>”这个更改,因为我认为之前的方法对更多人来说更清晰。谢谢!=D - Eduardo Lucio
1
不应该将同一个文件提供给iconv作为输入和输出 https://unix.stackexchange.com/questions/10241/how-can-i-make-iconv-replace-the-input-file-with-the-converted-output/10243#10243 https://dev59.com/cHTYa4cB1Zd3GeqP0Ouo - rofrol
1
我看到建议使用原地转换iconv iconv -f UTF-32 -t UTF-8 file.csv https://dev59.com/03VD5IYBdhLWcg3wKoSH#38rnnYgBc1ULPQZFlIsl - rofrol

1

使用iconv和uchardet(感谢farseerfc)

fish shell

cat your_file  | iconv -f (uchardet your_file ) -t UTF-8

bash shell

cat your_file  | iconv -f $(uchardet your_file ) -t UTF-8

如果使用Bash脚本

#!/usr/bin/bash
for fn in "$@"
do
    iconv < "$fn" -f $(uchardet "$fn") -t utf8
done

来自 Ubuntu 群组的 @flowinglight。


0

enca 命令无法处理我的 GB2312 编码的简体中文文本文件。

相反,我使用以下函数来为我转换文本文件。当然,您可以将输出重定向到文件中。

它需要 chardeticonv 命令。

detection_cat () 
{
    DET_OUT=$(chardet $1);
    ENC=$(echo $DET_OUT | sed "s|^.*: \(.*\) (confid.*$|\1|");
    iconv -f $ENC $1
}

0

查看在Linux命令行中可用的数据转换工具:https://www.debian.org/doc/manuals/debian-reference/ch11.en.html

此外,有一个任务是找出iconv中可用的所有编码列表。只需运行iconv --list,并发现编码名称与uchardet工具返回的名称不同(例如:uchardet中的x-mac-cyrillic与iconv中的mac-cyrillic)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接