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

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

新規登録して質問してみよう
ただいま回答率
85.50%
C++

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

Q&A

解決済

5回答

2596閲覧

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

_volatile

総合スコア13

C++

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

0グッド

1クリップ

投稿2018/03/23 07:09

前提・実現したいこと

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

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

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

該当のソースコード

C++

1#include <vector> 2#include <iostream> 3 4void foo(const char* String) { 5 std::vector<char*> vec; 6 7 for (int i = 0; String[i] != '\0'; ++i) { 8 int len = mblen(&String[i], MB_CUR_MAX); 9 char* buf; 10 memcpy(&buf, &String[i], len); 11 vec.push_back(buf); 12 } 13 14 for (const auto &c : vec) { 15 //Exception thrown at 0x0F9AD6CC (ucrtbased.dll) in 16 //Project1.exe: 0xC0000005: Access violation reading location 0xCCCCCC82. 17 std::cout << c << " "; 18 } 19} 20 21 22int main() { 23 foo("あのイーハトーヴォの透き通った風"); 24}

開発環境はVisual Studio 2017です。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

cpp

1#include <vector> 2#include <iostream> 3#include <cstring> 4#include <cstdlib> 5#include <clocale> 6 7void foo(const char* String) { 8 std::vector<char*> vec; 9 for (int i = 0; String[i] != '\0';) { 10 int len = mblen(&String[i], MB_CUR_MAX); 11 char* buf = new char[len+1]; 12 buf[len] = 0; 13 memcpy(buf, &String[i], len); 14 vec.push_back(buf); 15 i+=len; 16 } 17 18 for (const auto &c : vec) { 19 std::cout << c << ","; 20 } 21} 22 23 24int main() { 25 setlocale(LC_ALL, ""); 26 foo("あのイーハトーヴォの透き通った風"); 27}

追記

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

cpp

1#include <vector> 2#include <array> 3#include <iostream> 4#include <cstring> 5#include <cstdlib> 6#include <clocale> 7 8template<size_t N> 9std::ostream& operator<<(std::ostream& os, const std::array<char, N>& ary) 10{ os << ary.data();return os; } 11 12void foo(const char* String) { 13 std::vector<std::array<char, 3>> vec; 14 setlocale(LC_ALL, ""); 15 for (int i = 0; String[i] != '\0';) { 16 int len = mblen(&String[i], MB_CUR_MAX); 17 auto buf = std::array<char, 3>{0}; 18 memcpy(buf.data(), &String[i], len); 19 vec.push_back(std::move(buf)); 20 i+=len; 21 } 22 23 for (const auto &c : vec) { 24 std::cout << c << ","; 25 } 26} 27 28 29int main() { 30 foo("あのイーハトーヴォの透き通った風"); 31}

投稿2018/03/23 09:55

編集2018/03/23 14:35
asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

_volatile

2018/03/23 10:01

あああ!期待通りの出力が得られました!! asmさん、その他皆さんに感謝します!
_volatile

2018/03/23 11:27

すみませんがよければもう一つ質問を…… メモリリークの対策でbufの開放(delete)はどこ行うべきでしょうか? for文の最後辺りにつけると何故か出力が文字化けしてしまいどうすればいいか分からずです……。
asm

2018/03/23 12:25

なぜかってそりゃ「もういらない」って言ったデータを参照しようとしたらそうなります。 vecがいらなくなった時に行うべきですね。 for (const auto &c : vec) { delete[] c }
_volatile

2018/03/24 02:27

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

2018/03/24 03:17

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

2018/03/24 03:18

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

0

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

C++

1#include <string> 2#include <vector> 3#include <stdio.h> 4#include <tchar.h> 5#include <stdlib.h> 6#include <string.h> 7#include <locale.h> 8 9typedef std::vector<std::string> CHRS; 10 11void split932(const char* str, CHRS &chrs) 12{ 13 _locale_t locale = _create_locale(LC_CTYPE, "Japanese_Japan.932"); // mblen系には必要 14 15 int max = strlen(str); 16 _mblen_l(NULL, 0, locale); 17 while (*str) { 18 // 文字長を取得 19 int len = _mblen_l(str, max, locale); 20 if (len <= 0) break; 21 22 // 1文字追加 23 char buf[8]; // 適当 24 memset(buf, 0, sizeof(buf)); 25 strncpy(buf, str, len); 26 chrs.push_back(buf); 27 28 max -= len; 29 str += len; 30 } 31 _free_locale(locale); 32} 33 34void print(const CHRS &chrs) 35{ 36 for (const auto &ch : chrs) { 37 int l = ch.length(); 38 for (char c : ch) { 39 _tprintf(_T("[%X]"), (unsigned char)c); 40 } 41 _tprintf(_T("\n")); 42 } 43} 44 45int main() { 46 47 char s1[] = {0x31, 0x82, 0xA0, 0x32, 0x00}; // 1あ2 48 CHRS chrs; 49 split932( s1, chrs); 50 print(chrs); 51 52 return 0; 53}

投稿2018/03/23 09:54

can110

総合スコア38234

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

投稿2018/03/23 08:30

PineMatsu

総合スコア3579

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

char* buf;

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

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

C++

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

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

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

投稿2018/03/23 07:20

kazto

総合スコア7196

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

_volatile

2018/03/23 07:38

解凍ありがとうございます。例外はなくなりました。 ただ、意図した出力にはならないようです……。 std::stringに関しては使いたかったですがWin32APIプログラミングの都合上 後々変換やらが面倒でネックとなってしまったのであえて使用しませんでした…
kazto

2018/03/23 08:18

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

2018/03/23 08:23

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

2018/03/23 08:35

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

0

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

投稿2018/03/23 07:14

koizumi

総合スコア230

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

_volatile

2018/03/23 07:19

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問