質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C++

    3468questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual Studio

    1838questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • Windows

    1447questions

    Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

C++17でUTF-8Nのファイルを出力したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 376

Kyun001

score 15

 質問

C++17でUTF-8(BOMなし)のファイルを出力したいです。

 試したこと1

このプログラムの場合0バイトのout.txtが作成されました。

#include <iostream>
#include <string>
#include <fstream>

int main() {
  std::wofstream ofs("out.txt");
  ofs << L"あいうえお" << std::endl;
  ofs.close();
}

 試したこと2

このプログラムで問題なく出力できましたが警告が出ています。警告をなくしたいです。

また、#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNINGまたは
#define _SILENCE_ALL_CXX17_DEPRECATION_WARNINGSすると警告は出ませんができれば他の方法で解決したいです。

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <codecvt>

int main() {
  std::wofstream ofs("out.txt");
  // C4996 warning STL4017
  ofs.imbue(std::locale(std::locale(), new std::codecvt_utf8<wchar_t>));

  ofs << L"あいうえお" << std::endl;

  ofs.close();
}

 試したこと3

WideCharToMultiByte を使用する方法として、以下の記事を参考にして作成しましたが、16バイト目に無駄なNULL(0x00)が含まれていました。
http://nekko1119.hatenablog.com/entry/2017/01/02/054629


※out.txtがプログラムで作成したテキストファイル。out2.txtがテキストエディタを使いUTF-8(BOMなし)で「あいうえお」という文字列を書き込んだファイル。

#include <Windows.h>

#include <iostream>
#include <vector>
#include <string>
#include <fstream>

std::string wide_to_utf8_winapi(std::wstring const& src)
{
  auto const dest_size = ::WideCharToMultiByte(CP_UTF8, 0U, src.data(), -1, nullptr, 0, nullptr, nullptr);
  std::vector<char> dest(dest_size, '\0');
  if (::WideCharToMultiByte(CP_UTF8, 0U, src.data(), -1, dest.data(), dest.size(), nullptr, nullptr) == 0) {
    throw std::system_error{ static_cast<int>(::GetLastError()), std::system_category() };
  }
  return std::string(dest.begin(), dest.end());
}

int main() {
  std::ofstream ofs("out.txt");

  ofs << wide_to_utf8_winapi(L"あいうえお") << std::endl;

  ofs.close();
}

 環境

OS Windows 10 64bit 1709
IDE Visual Studio 2017
Windows SDK Version 10.0.17134.0
文字セット Unicode 文字セットを使用する
PlatformToolSet Visual Studio 2017 (v141)
C++言語標準 ISO C++17 Standard (/std:c++17)
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

普通に

#include<fstream>

int main()
{
    std::ofstream ofs("out.txt");
    ofs << u8"ありきたり" << std::endl;
}

でいけなかったっけか。


しかし、固定文字列ではなく

あー、そりゃそうだ。失礼、どうも寝ぼけていたようで。

WideCharToMultiByte を使用する方法として、以下の記事を参考にして作成しましたが、16バイト目に無駄なNULL(0x00)が含まれていました。

まずUTF-8なbyte列で「ありうえお」を表現すると

 E3 81 82 E3 81 84 E3 81 86 E3 81 88 E3 81 8A

のようになります。

ここで

out2.txtがテキストエディタを使いUTF-8(BOMなし)で「あいうえお」という文字列を書き込んだファイル。 

の出力と見比べると

 0D 0A

が余計についている事がわかりますが、これは言うまでもなくCR+LFですね(改行コード)。

out.txtがプログラムで作成したテキストファイル。

の出力のこれも`std::endlからきているものですね。

さて、NULL文字が含まれる原因はwide_to_utf8_winapiの実装がだめだからです。

修正版がこちらです。

#include <string>
#include <windows.h>
#include <fstream>
#include <system_error>
std::string utf_16_to_utf_8(const std::wstring& str) {
    static_assert(sizeof(wchar_t) == 2, "this function is windows only");
    const int len = ::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, nullptr, 0, nullptr, nullptr);
    std::string re(len * 2, '\0');
    if (!::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, &re[0], len, nullptr, nullptr)) {
        const auto er = ::GetLastError();
        throw std::system_error(std::error_code(er, std::system_category()), "WideCharToMultiByte:(" + std::to_string(er) + ')');
    }
    const std::size_t real_len = std::char_traits<char>::length(re.c_str());
    re.resize(real_len);
    re.shrink_to_fit();
    return re;
}
int main() {
    std::ofstream ofs("out.txt");

    ofs << utf_16_to_utf_8(L"あいうえお") << std::endl;
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/24 11:47

    ご回答有り難うございます。
    書いていただいたプログラムは期待する結果が得られました。
    しかし、固定文字列ではなく変数の場合はどのように書けば良いでしょうか。

    以下のように書いた場合 u8str という変数名を指定している事になる為コンパイルエラーとなります。

    int main() {
    std::ofstream ofs("out.txt");
    auto str = L"あいうえお";
    ofs << u8str << std::endl;
    }

    質問内容が適切でなく申し訳ありません。

    キャンセル

  • 2018/05/24 13:14

    ありがとうございます。
    期待する結果を得ることが出来ました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C++

    3468questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Visual Studio

    1838questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • Windows

    1447questions

    Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。