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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

3回答

2459閲覧

→以降の文字列のみを表示できたのですが、文字化けしてしまいます。

KZK13

総合スコア43

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2020/07/04 20:16

編集2022/01/12 10:55

環境はWindows10、visual stuido 2019です。

「範馬裕次郎→最強」という文字列で→以降の文字列のみを表示できたのですが、以下のように文字化けしてしまいます。

C:\Users>C:\Users\source\repos\ConsoleApplication3\x64\Release\ConsoleApplication3.exe 範馬裕次郎→最強 ィ最強

以下がコードです。

#pragma warning(disable: 4996) #include <stdio.h> #include <string.h> int main() { char str1[128] = "範馬裕次郎→最強"; char str2[128]; char* p; p = strchr(str1, '→');//ここで→以降の文字をbuffer3に入れる。 strcpy(str2, p); printf("%s\n%s", str1, str2); return 0; }

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

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

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

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

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

guest

回答3

0

この回答はCおよび一般的な文字コードの知識を前提とし、この質問を読んだ質問者以外の人が参考するために記載しています。申し訳ありませんが、質問者には理解できない部分が多数あると思われますので、あらかじめご了承ください。


Visual Studio 2019のC++コンパイラ(cl.exe、以下cl)は、標準で、実行環境で設定されている言語でのANSI文字コードを内部文字コードに設定します。日本語環境であれば、Windows-31J(CP932)です。これは、ソースコードがWindows-31J、BOM付きUTF-8、BOM付きUTF-16LE、BOM付きUTF-16BEのいずれであっても同じです(文字コード認識のためにUTF-8であってもBOMは必須です。これ以外の文字コードをclは自動認識できません)。ですので、Windows-31Jでどうなっているかを確認していく必要があります。

問題は下記の部分です。

p = strchr(str1, '→');//ここで→以降の文字をbuffer3に入れる。

buffer3が何を意味しているのか不明ですので、コメントは無視します。'→'がいくつになるのかを確認しなければなりません。

まずは一般的なCについてのリファレンスを見てみましょう。

文字定数 - cppreference.com

'文字'という表現はchar型の値を扱うための文字リテラルです。リテラルの型自体はintですが、それはcharでの文字がそのままマッピングしたものとなります。このとき注意しなくてはならないのはchar一つで表せない文字だったときの動作です。'A'(41)や'ア'(B1)のようにWindows-31Jでchar一つで表現できる文字は問題ありませんが、'あ'(82 A0)のようないわゆる2バイト文字はchar一つで表現できません。この場合、リテラルが何になるのかは処理系定義となっています。

と言うことでMSDNでMicrosoftの処理系ではどうなっているのかを見てみましょう。

文字列リテラルと文字リテラルC++() | Microsoft Docs

cppreferenceの説明とちょっと違う部分がありますが、どうやら、マルチバイト文字であってもそのまま上位からくっつけて、intにするようです。シングルバイトでの文字が複数の場合でもintに収まる限りはそのまま上位から下位までの一つの文字として扱うとなっています。

|文字リテラル|Windows-31J|リテラル値|バイト表示|
|---|---|---|
|'A'|41|65|00000041|
|'ア'|B1|-79|FFFFFFB1|
|'あ'|82 A0|33440|000082A0|
|'→'|81 A8|33192|000081A8|

'ア'のように上位ビットがあるとMicrosoftの処理系ではcharが負の値になるため、intとしては負の値になることに注意してください。

これで'→'33192であることが分かりました。次はstrchr()です。リファレンスを見てみましょう。

strchr - cppreference.com

'→'intですので型としては問題ないように思いますが、注意事項があります。

...内の ch ((char)ch によって行われたかのように char に変換した後) が...

この一文です。つまり、intを受け取るが、実際は(char)chcharにキャストするかのようになります。(char)33192-88です。16進数ですとA8となります。strchr()は各文字をunsigned charとして解釈しくいき、その中でA8のバイトがあるところを探すとなります。検索対象の文字列をバイト表記にすると次のようになります。

str1 範 馬 裕 次 郎 → 最 強 94 CD 94 6E 97 54 8E 9F 98 59 81 A8 8D C5 8B AD ↑

ここがpです。ここまで来れば分かりますね。strcpy()p以降をコピーとなりますので、str2は次のようになると言うことです。

str2 ィ 最 強 A8 8D C5 8B AD ↑

Windows-31Jで(A8)のみは"ィ"です。よって最終的に表示されるのも"ィ最強"となるわけです。

strchr()関数や'文字'リテラルはASCIIのみといったシングルバイト文字を前提に作られています。Windows-31Jのようなマルチバイト文字の場合は想定した通りの動作はできません。ASCII以外の文字を扱いたい場合は、ワイド文字やUnicode文字を使う、文字列として処理するなどしないとむずかしいでしょう。ただ、ワイド文字であってもUTF-16の場合はサロゲートペアという罠があるため、絵文字だけ文字化けとかのバグが起きる場合があります。

内部文字コードがUnicodeに統一されていて一つの方法ぐらいしか用意されていない他言語と違い、Cでは様々な文字コードを直接バイト列として扱えたり、マルチバイト文字としてもさまざまな手段が用意されています。その分、文字コードの正しい知識が無いと、ASCII以外の文字、つまりは漢字や平仮名といった普通の日本語の文字ですら正しく扱うことはできません。皆さん、日本語は注意して扱いましょうね。

投稿2020/07/04 22:23

編集2020/07/05 04:54
raccy

総合スコア21733

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

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

0

元のソースと比較して見ましょうv^^

c

1#include <stdio.h> 2#include <string.h> 3 4int main( ) 5{ 6 7 char str1[128] = "範馬裕次郎→最強"; 8 9 char str2[128]; 10 11 char *p; 12 13 p = strstr(str1, "→"); //ここで→以降の文字をbuffer3に入れる。 14 15 strcpy(str2, p); 16 17 printf("%s\n%s\n", str1, str2); 18 19 return 0; 20}

結果

text

1usr ~/Project/test % ./a.out 2範馬裕次郎→最強 3→最強 4

投稿2020/07/04 20:25

編集2020/07/04 20:29
cateye

総合スコア6851

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

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

cateye

2020/07/04 20:38 編集

error: character too large for enclosing character literal type p = strchr(str1, '→'); //ここで→以降の文字をbuffer3に入れる。 warning: incompatible pointer to integer conversion passing 'char [4]' to parameter of type 'int' [-Wint-conversion] ・・・clangではエラーに成りました。 ・・・コメントも間違っています。また、ワーニングは問題を指摘してくれます、見ないふりは止めましょう。
KZK13

2020/07/04 20:59

解答ありがとうございます。 >>ワーニングは問題を指摘してくれます、見ないふりは止めましょう。 なぜかこちらの方では警告が出来ませんでした。警告が出やすいような設定に変えます。 あの、「→」までも文字列に含んでしまうのですが、→以降の文字だけをstr2に入れられないでしょうか。
cateye

2020/07/04 21:07

>→以降の文字だけ・・・ こちらの環境(UTF-8)では、 p = strstr(str1, "→");のあと p += 3;とすることで表示できています。 usr ~/Project/test % ./a.out 範馬裕次郎→最強 最強
KZK13

2020/07/04 21:20

なるほど、→のバイト分だけずらせばいいのですね! 私の方ではp += 2;で正常に表せました。 どうもありがとうございます。
KZK13

2020/07/04 21:22

結論として、strchr(str1, '→');の部分をstrstr(str1, "→"); としたことで問題が解決できたと思うのですが、 この認識で正しいでしょうか? どうかよろしくお願いいたします。
cateye

2020/07/04 21:29

その通りです。 ”→”は多バイト文字(UTF-8なら3バイト)なのでアドレスを渡して strstr()を使いました。
pepperleaf

2020/07/05 00:01

Visual Studio2019だと、Warningは出ないようですね。この辺は、raccyさんの回答にありますね。(初心者マークへの回答としては? ですが) C言語の場合、半角英数以外の扱いは要注意(元々、考慮されていない)と言うのが、ポイントでしょうか。
kazuma-s

2020/07/05 04:21

特殊な例ですが、strstr でダメな場合があります。 "範馬裕次郎→最強" を "風吹ィチロウ→最強" に変えてみてください。 'ィ' は半角の小文字の「ィ」です。
cateye

2020/07/05 05:18 編集

UTF-8環境では問題なしです。>kazuma-sさん usr ~/Project/test % ./a.out 風吹ィチロウ→最強 最強
guest

0

バイト単位で比較する strstr ではなく、
文字単位で比較する sjis_strstr を作ってみました。

C

1#pragma warning(disable: 4996) 2#include <stdio.h> 3#include <string.h> // strlen, strncmp, strcpy 4#include <windows.h> // IsDBCSLeadByte 5 6char *sjis_strstr(const char *s1, const char *s2) 7{ 8 for (int n = strlen(s2); *s1; s1 += IsDBCSLeadByte(*s1) ? 2 : 1) 9 if (strncmp(s1, s2, n) == 0) return (char *)s1; 10 return NULL; 11} 12 13int main() 14{ 15 16 char str1[128] = "範馬裕次郎→最強"; 17 char str2[128]; 18 char* p; 19 p = sjis_strstr(str1, "→"); 20 strcpy(str2, p); 21 printf("%s\n%s\n", str1, str2); 22 23 char str3[] = "風吹ィチロウ→最強"; 24 p = sjis_strstr(str3, "→"); 25 strcpy(str2, p); 26 printf("%s\n%s\n", str3, str2); 27 28 return 0; 29}

投稿2020/07/05 04:36

kazuma-s

総合スコア8224

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問