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

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

ただいまの
回答率

89.13%

マルチバイト文字のchar*をvector<char*>に変換したい

解決済

回答 5

投稿

  • 評価
  • クリップ 1
  • VIEW 4,243

_volatile

score 13

 前提・実現したいこと

マルチバイト文字が格納されているchar*型の文字列をvector<char*>に変換したいです。

char* String = "abcあいうえお"
↓変換後
vector<char*> vec = {"a", "b", "c", "あ", "い", "う", "え", "お"}

このような風にしたいのですが、マルチバイト文字が厄介で上手く文字を分割できず、例外が発生して処理が中断されてしまいます。
memcpy辺りに問題があるとは思いますがどうすればいいかわかりません。助言をお願いします……

 該当のソースコード

#include <vector>
#include <iostream>

void foo(const char* String) {
    std::vector<char*> vec;

    for (int i = 0; String[i] != '\0'; ++i) { 
        int len = mblen(&String[i], MB_CUR_MAX);
        char* buf;
        memcpy(&buf, &String[i], len);
        vec.push_back(buf);
    }

    for (const auto &c : vec) {
        //Exception thrown at 0x0F9AD6CC (ucrtbased.dll) in 
        //Project1.exe: 0xC0000005: Access violation reading location 0xCCCCCC82.
        std::cout << c << " ";
    }
}


int main() {
    foo("あのイーハトーヴォの透き通った風");
}

開発環境はVisual Studio 2017です。

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 5

checkベストアンサー

+1

#include <vector>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <clocale>

void foo(const char* String) {
    std::vector<char*> vec;
    for (int i = 0; String[i] != '\0';) { 
        int len = mblen(&String[i], MB_CUR_MAX);
        char* buf = new char[len+1];
        buf[len] = 0;
        memcpy(buf, &String[i], len);
        vec.push_back(buf);
        i+=len;
    }

    for (const auto &c : vec) {
        std::cout << c << ",";
    }
}


int main() {
    setlocale(LC_ALL, "");
    foo("あのイーハトーヴォの透き通った風");
}

追記

面倒な解放をしたくない場合(C++11以降用)
SJIS決め打ちするとmblenは1~2byteなので'\0'含めて3byteを固定で持てばいい

#include <vector>
#include <array>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <clocale>

template<size_t N>
std::ostream& operator<<(std::ostream& os, const std::array<char, N>& ary)
{ os << ary.data();return os; }

void foo(const char* String) {
    std::vector<std::array<char, 3>> vec;
    setlocale(LC_ALL, "");
    for (int i = 0; String[i] != '\0';) { 
        int len = mblen(&String[i], MB_CUR_MAX);
        auto buf = std::array<char, 3>{0};
        memcpy(buf.data(), &String[i], len);
        vec.push_back(std::move(buf));
        i+=len;
    }

    for (const auto &c : vec) {
        std::cout << c << ",";
    }
}


int main() {
    foo("あのイーハトーヴォの透き通った風");
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/24 11:27

    ありがとうございます!!(*'▽')

    キャンセル

  • 2018/03/24 12:17

    解放をしたくないとき... の例ですが、 buf は for 文の中でしか有効でないので、そのポインタを for の外側に持ち込むことはできないと思います。

    キャンセル

  • 2018/03/24 12:18

    すみません、勘違いでした。失礼しました。

    キャンセル

0

for文の条件が、i++じゃないとか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/23 16:19

    どうやら++iをi++にしても例外が発生するようです……
    memcpy(&buf, &String[i], len);をコメントアウトすると例外は消えるみたいですが……

    キャンセル

0

提示のコードですが、誤りがあります。

char* buf;
memcpy(&buf, &String[i], len);

上記箇所で、メモリ確保していない不定な領域に対してmemcpyを実施し、メモリ破壊しています。
もし提示のコードの意図のまま進めるなら、

char* buf;
/* メモリ確保! */
buf = (char *)malloc(sizeof(char) * len);
memcpy(&buf, &String[i], len);

上記のようにメモリ確保を行う必要があります。

それとは別に、私ならstd::string使って、char*とかのポインタ操作を行わないような設計にします。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/23 16:38

    解凍ありがとうございます。例外はなくなりました。
    ただ、意図した出力にはならないようです……。

    std::stringに関しては使いたかったですがWin32APIプログラミングの都合上
    後々変換やらが面倒でネックとなってしまったのであえて使用しませんでした…

    キャンセル

  • 2018/03/23 17:18

    意図した出力がどうなれば良いのか、追記をお願いします。

    キャンセル

  • 2018/03/23 17:23

    forの++iを消して
    for末尾にi+=lenしないとダメだね

    キャンセル

  • 2018/03/23 17:35

    今回の場合はコンソール画面に
    「あ の イ ー ハ ト ー ヴ ォ の 透 き 通 っ た 風」と出力されればOKなのですが、
    「テク堙兄9I a^セO T ンンンx^ヲO ^」O L ^」O  ンンンンンンンンンンンンンンンンンンンンンンンンンンンンン ョ  ヘ  」 テテ テー霤」のように文字化けして出力されます。
    asmさんありがとうございます。
    しかし出力は文字化けしたままです…

    キャンセル

0

文字コードによって処理が変わると思います。
取り敢えずUTF-8限定ですが、以下のサイトに処理例が載っていました。

マルチバイト文字列を1文字ずつに分割する

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

結果はstd::vector<std::string>に格納するようにしました。
マルチバイトのエンコーディングはcp932限定ですが、プロジェクト設定の文字セットUNICODEorマルチバイト両対応してみました。

#include <string>
#include <vector>
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>

typedef std::vector<std::string> CHRS;

void split932(const char* str, CHRS &chrs)
{
    _locale_t locale = _create_locale(LC_CTYPE, "Japanese_Japan.932");    // mblen系には必要

    int max = strlen(str);
    _mblen_l(NULL, 0, locale);
    while (*str) {
        // 文字長を取得
        int len = _mblen_l(str, max, locale);
        if (len <= 0) break;

        // 1文字追加
        char buf[8];    // 適当
        memset(buf, 0, sizeof(buf));
        strncpy(buf, str, len);
        chrs.push_back(buf);

        max -= len;
        str += len;
    }
    _free_locale(locale);
}

void print(const CHRS &chrs)
{
    for (const auto &ch : chrs) {
        int l = ch.length();
        for (char c : ch) {
            _tprintf(_T("[%X]"), (unsigned char)c);
        }
        _tprintf(_T("\n"));
    }
}

int main() {

    char s1[] = {0x31, 0x82, 0xA0, 0x32, 0x00}; // 1あ2
    CHRS chrs;
    split932( s1, chrs);
    print(chrs);

    return 0;
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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