文字コード変換

時々必要になるんだけどいつも忘れて調べなおすのでメモ。
WindowsではAPI、それ以外ではiconvが環境に備わっているのでそのまま使える。
ただ、WindowsAPIはcp932とutf8とucs2?くらいしか使えないのでそれ以外が要るときはなんか用意する必要がある。
iconvはiconvで改行の面倒を見てくれないので用途によっては不便になる。

windows

#include <windows.h>
#include <string>
#include <iostream>
#include <vector>

// const char *text -> const std::string &textに変更など地味に修正

std::wstring to_WideChar(UINT uCodePage, const std::string &text)
{
  int size=MultiByteToWideChar(uCodePage, 0, text.c_str(), -1, NULL, 0);
  std::vector<wchar_t> buf(size);
  size=MultiByteToWideChar(uCodePage, 0, text.c_str(), -1, &buf[0], buf.size());
  return std::wstring(buf.begin(), buf.begin()+size);
}

std::string to_MultiByte(UINT uCodePage, const std::wstring &text)
{
  int size=WideCharToMultiByte(uCodePage, 0, text.c_str(), -1, NULL, 0, 0, NULL);
  std::vector<char> buf(size);
  size=WideCharToMultiByte(uCodePage, 0, text.c_str(), -1, &buf[0], buf.size(), 0, NULL);
  return std::string(buf.begin(), buf.begin()+size);
}

std::string cp932_to_utf8(const std::string &text)
{
  return to_MultiByte(CP_UTF8, to_WideChar(CP_OEMCP, text));
}

int main(int argc, char **argv)
{
  std::string buf;
  // 標準入力を変換する
  while(std::cin){
    std::getline(std::cin, buf);
    std::cout << cp932_to_utf8(buf) << std::endl;
  }

  return 0;
}
> main.exe < cp932.txt > utf8.txt

iconv

#include <iconv.h>
#include <string>
#include <iostream>
#include <vector>

std::string convert(const char *text, const char *fromcode, const char *tocode)
{
  iconv_t cd=iconv_open(tocode, fromcode);
  if(cd==(iconv_t)-1){
    std::cerr << "fail to iconv_open: " << fromcode << " --> " << tocode << std::endl;
    return "";
  }

  // inbuf
  size_t inbytesleft=std::strlen(text);
  char *inbuf=const_cast<char*>(text);
  // outbuf
  std::vector<char> buf;
  size_t pos=0; // 変換済み文字の最後尾
  size_t outbytesleft=0;
  char *outbuf=NULL;

  while(inbytesleft){
    buf.resize(buf.size()+inbytesleft*2); // とりあえず2倍確保
    outbuf=&buf[pos];
    outbytesleft=buf.size()-pos;
    size_t ret=iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
    if(ret==-1){
      std::cerr << "fail to iconv" <<  std::endl;
      return "";
    }
    pos=outbuf-&buf[0];
  }
  if(outbytesleft==0){
    buf.push_back('\0');
  }
  iconv_close(cd);

  return &buf[0];
}

int main(int argc, char **argv)
{
  if(argc!=3){
    std::cerr << "usage: " << argv[0] << " {fromcode} {tocode}" << std::endl;
    return 1;
  }

  std::string buf;
  // 標準入力を変換する
  while(std::cin){
    std::getline(std::cin, buf);
    std::cout << convert(buf.c_str(), argv[1], argv[2]) << std::endl;
  }

  return 0;
}
$ a.out cp932 utf8 < cp932.txt > utf8.txt