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

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

新規登録して質問してみよう
ただいま回答率
85.35%
Visual Studio

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

C++

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

6回答

6132閲覧

文字列から1文字だけを取り出す

aus98266

総合スコア0

Visual Studio

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

C++

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

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2020/05/29 13:26

編集2020/05/29 17:26

Microsoft Visual Studioで作っています

buffにchar str[200] = "あいうえお";のうだけを取り出して入れたいのですが実行すると
「うと表示されてしまいます。
「はどこからきたのでしょうか?教えてほしいです。また表示させない方法もお願いします。
#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<math.h>

int main(void) {

char str[200] = "あいうえお"; char buff[1000]; strncpy(buff, str+3,3 ); printf("%s\r\n", buff); return 0;

}

追記
C++では文字列から1文字だけを取り出すのは不可能なのでしょうか?

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

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

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

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

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

guest

回答6

0

そもそもchar型はたったの8bitしかありません。人類史上何度となく文字を固定長で表そうとする試みが続けられてきました。しかしそのことごとくは失敗に終わっています。常に文字は可変長のメモリーを必要とすることをおさえてください。

しかもさらに驚くべきことに、「文字」の定義はなんと4通りも存在します。何byteか、何単位か、何コードポイントか、何書記素クラスタか、の4つです。
C++標準化委員会、ついに文字とは何かを理解する: char8_t#Unicodeにおける4つの文字の定義
自分が意図している文字の定義はいったいどれなのか把握する必要があります。

文字は直接は計算機で扱えません。そこで特定のbyte列に変換して取り扱います。この方法が文字エンコードです。近年日本語圏で使われているのはUTF-8(cp65001)、ついでShift-JIS(cp932)です。まずはどの文字エンコードでエンコードされたbyte列を取り扱おうとしているかを把握する必要があります。

とはいえ、Visual Studioに付属のC++コンパイラであるcl.exeに限定してもなお、コンパイラオプション次第で多様な文字エンコードを利用できます。

ここまで見てくればわかるように、コンピュータで文字を取り扱うのは全くかんたんではありません。たかが「あいうえお」が何文字かを考えるだけでも考慮しないといけないことが山のようにあるのです。

ここで文字エンコードがUTF-8であることを保証された文字(列)リテラルがC++11/C++17で追加されています。これを使ってみましょう。

cpp

1//今まで使ってきたもの 2"あいうえお";//長さは実装依存、const char[N]型 3//今回紹介するもの 4u8"あいうえお";//UTF-8でエンコードされている。C++17まではconst char[16]型、C++20からconst char8_t[16]型

さて、今回はひとまず一文字の定義を何codepointか、にすることとしましょう。書記素クラスタまで考えると難しすぎます。絵文字とか濁点がついたものがとりあえず出てこないのでこれで十分でしょう

UTF-8は先頭のbyteを検査することでその1文字が何byte使用しているかわかるようになっています。この性質を利用して質問にあったことを実現してみましょう。

cpp

1#include <iostream> 2#include <cstring> 3#if !defined(__cpp_char8_t) 4using char8_t = char; 5#endif 6std::size_t charlen(const char8_t* s) 7{ 8 if (*s == 0) { 9 } else if ((*s & 0x80) == 0) { 10 return 1u; 11 } else if ((*s & 0xE0) == 0xC0) { 12 return 2u; 13 } else if ((*s & 0xF0) == 0xE0) { 14 return 3u; 15 } else if ((*s & 0xF8) == 0xF0) { 16 return 4u; 17 } 18 return 0; 19} 20int main() 21{ 22 const auto str = u8"あいうえお"; 23 std::size_t front_i = 0, len = 0; 24 for (std::size_t i = 0; i < 2; ++i, front_i += len = charlen(&str[front_i])); 25 char8_t dest[5]; 26 std::memcpy(dest, &str[front_i], len); 27 dest[len] = u8'\0'; 28 //コンソールの文字エンコードがUTF-8だと仮定する 29 std::cout << reinterpret_cast<const char*>(dest) << std::endl; 30}

https://wandbox.org/permlink/BIkWAMmrI0t1KHa6

Windowsで動作させるときはWindows 10 1607よりあとのOSで、あらかじめchcp 65001したコマンドプロンプトで実行してください。

なお余談ですが、C++がCから受け継いだ文字列操作系関数はいくつもありますが、このうち多くの関数が安全に使うのが難しいものです。実際MSVCでは_CRT_SECURE_NO_WARNINGSなしではそれらの関数に警告を出すはずです。なのでもしC言語のような文字操作をするなら、memcpyを利用すると良いです。strcpyと違い、文字数より一つ多くバッファを確保しなければならないことを忘れる懸念は自分で明示的にNULL文字を代入しますから減りますし、実際MSVCは何も言ってきません。

しかしまあ、せっかくC++を使うのですからもう少しだけC++っぽく扱ってみましょう。std::basic_stringとC++17で追加されたstd::basic_string_viewを使います。std::basic_string_viewを使ってstd::basic_stringを構築するときに勝手にNULL文字終端する性質を利用しています。

cpp

1#include <iostream> 2#include <string> 3#include <string_view> 4#include <stdexcept> 5#if !defined(__cpp_char8_t) 6using char8_t = char; 7#endif 8std::size_t charlen(const char8_t* s) 9{ 10 if (*s == 0) { 11 } else if ((*s & 0x80) == 0) { 12 return 1u; 13 } else if ((*s & 0xE0) == 0xC0) { 14 return 2u; 15 } else if ((*s & 0xF0) == 0xE0) { 16 return 3u; 17 } else if ((*s & 0xF8) == 0xF0) { 18 return 4u; 19 } 20 return 0; 21} 22auto extract_nth_char(std::basic_string_view<char8_t> s, std::size_t n) 23{ 24 std::size_t front_i = 0, len = 0, limit = n ? n - 1 : throw std::invalid_argument("0 is invalid"); 25 for (std::size_t i = 0; i < limit; ++i, front_i += len = charlen(&s.data()[front_i])); 26 return s.substr(front_i, len); 27} 28int main() 29{ 30 using namespace std::literals; 31 const auto str = u8"あいうえお"sv;//string_viewになる 32 //std::basic_stringに変換することでNULL終端する 33 const std::basic_string<char8_t> dest{extract_nth_char(str, 3)}; 34 //コンソールの文字エンコードがUTF-8だと仮定する 35 std::cout << reinterpret_cast<const char*>(dest.c_str()) << std::endl; 36}

https://wandbox.org/permlink/sSSbwj1fHIyB06jH

なおより本格的に扱う場合はicuというライブラリを利用します。

参照

投稿2020/05/30 12:56

編集2020/05/30 14:13
yumetodo

総合スコア5852

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

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

0

wchar_t1つで一文字を表せない文字を使用しないのであれば…

C

1#define _CRT_SECURE_NO_WARNINGS 2#include<stdio.h> 3#include <string.h> 4#include <locale.h> 5int main(void) { 6 setlocale(LC_ALL, "Japanese"); 7 wchar_t str[200] = L"あいうえお"; 8 wchar_t buff[1000] = {}; 9 wcsncpy(buff, str + 2, 1); 10 wprintf(L"%s\n", buff); 11 return 0; 12}

投稿2020/05/30 15:42

編集2020/05/30 16:02
SHOMI

総合スコア4079

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

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

yumetodo

2020/05/30 15:50 編集

補足しておくと、Windows環境でwchar_tといえばUTF-16を指します。wchar_t1つで一文字を表せないとは、ほしい一文字の定義が1コードポイントならば、サロゲートペアのことですね。
SHOMI

2020/05/30 16:00

補足ありがとうございます。
guest

0

Cが出来た頃は、コンピュータで扱う文字は当然にアルファベット、数字と一部記号だけでした。
なので、Cでは一文字というのはつまりそういう文字のことを意味します。
そして、char型は、それらの文字を格納するのに十分なサイズをもった整数型の変数、というのがCにおける定義です。

その後、コンピュータの能力は向上し応用範囲は広がって、英語圏以外の文字も扱えるようになりました。当然ながら、アルファベットにしか対応していないchar型の一文字分では、例えば日本語の一文字を格納することは不可能です。

ということで、日本語の一文字を一文字として扱いたいならば、C(その発展形としてのC++)での一文字(=アルファベット等)という扱いに頼らずに自分で「一文字」を定義し、処理する必要が出てきます。
その意味では、C++の「基本機能では」文字列から一文字を取り出すことは出来ない、と言っていいでしょう。

さらに面倒なことに、コンピュータが各国言語に対応するときの諸般の事情によって、日本語の文字とコンピュータ上で扱うときのデータ表の対応に複数のものが存在する、というややこしい状況になってしまいました。
自分で「一文字」を処理するにも、自分が扱おうとしている「日本語」はどういう対応表(文字コード表)に依っているかを知らないといけない、という状況になっています。

もちろん、いろいろあるとは言え文字コード表として流通するものには限られた種類しかありませんから、先人たちがそのへんの処理はさんざんやっていて「ライブラリ」としてまとまっているものもあります。プログラムを作るのが目的であればそういうライブラリを使うのがよいかと思います。

ただ、今回の質問については雰囲気が「学習用」に見えます。であるならば、上記の事情を自分で理解するために、日本語の「あいうえお」がコンピュータ上でどう扱われているのかを調べたうえで「一文字をとりだす」処理を書けるようになるのがあなたのミッションではないかと思うのですがいかがでしょうか。

投稿2020/05/29 23:07

thkana

総合スコア7703

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

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

0

C

1#include <stdio.h> // printf, putchar 2#include <stdlib.h> // mblen, MB_CUR_MAX 3#include <locale.h> // setlocale, LC_CTYPE 4 5int main(void) 6{ 7 char str[32] = "あいうえお"; 8 9 setlocale(LC_CTYPE, ""); 10 for (int n, i = 0; str[i]; i += n) { 11 n = mblen(str + i, MB_CUR_MAX); 12 printf("[%.*s] ", n, str + i); 13 } 14 putchar('\n'); 15}

C

1#include <stdio.h> // printf, putchar 2#include <string.h> // strlen 3#include <stdlib.h> // mbstowcs 4#include <locale.h> // setlocale 5 6int main(void) 7{ 8 char str[32] = "あいうえお"; 9 wchar_t wstr[32]; 10 11 setlocale(LC_CTYPE, ""); 12 mbstowcs(wstr, str, strlen(str) + 1); 13 for (int i = 0; wstr[i]; i++) 14 printf("[%lc] ", wstr[i]); 15 putchar('\n'); 16}

理解できない場合は、どこが分からないのかを質問してください。

投稿2020/05/31 02:58

kazuma-s

総合スコア8224

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

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

yumetodo

2020/05/31 12:07

localeはこわれている環境もあるので(mingwとかとか)まあ動くとは限らないでしょう。
guest

0

文字コードは、シフトJISのようですね。
この場合、str[200] は、
{ 0x80, 0xA0, 0x80, 0xA2, 0x80, 0xA4, 0x80, 0xA6, 0x80, 0xA8, 0x00 } となっています。

このデータに対して、strncpy()で、str[3]からstr[6]まで、つまり、
{ 0xA2, 0x80, 0xA4 } とbuffにコピーしています。
シフトJISで文字コード0xA2は、"「"(厳密には半角)になりますので、"「う"と表示されます。

修正は、以上の意味を確認できれば可能ですので、省略させていただきます。

投稿2020/05/29 14:27

YT0014

総合スコア1750

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

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

aus98266

2020/05/29 14:54

char str[200] = "あいうえお" これでchar strの0~4までの配列にあいうえおを入れていることにはならないんですね
YT0014

2020/05/29 15:38

厳密に言うと、ご利用のC++のコンパイラで、シフトJISを用いる場合、に限定されます。 charは、8ビットとは限らないですし、UTF-8の場合、'あ'は、0xE3,0x81,0x82と3バイトになります。 C 及び C++ は、OSを作成する為の言語として作られた経緯があるため、CPUの仕様に合うように、charやintのサイズを調整することが多いです。
aus98266

2020/05/29 17:19

じゃあ上のコードのstrncpy()を変えて”う”の部分の文字コードだけをコピーしてそれを変換して”う”にすればよいのですね
YT0014

2020/05/30 22:29

strncpy()の引数を調整するだけで、変換は不要です。 文字コード、というのは、我々が文字と思っているデータの数字としての側面です。
guest

0

文字コードは何でしょうか?

  • シフトJISの場合、str+3の2バイト目に当たります。
  • UTF-8の場合、str+3の位置です。

どちらにしても、を取り出すという動作はしないかと思います。

投稿2020/05/29 13:28

maisumakun

総合スコア146018

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

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

aus98266

2020/05/29 14:56

うという文字を取り出すにはどうしたらよいのでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問