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

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

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

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

Q&A

5回答

5879閲覧

C++において、文字列から10バイト目の文字を取得したい

ryo_se

総合スコア68

C++

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

0グッド

0クリップ

投稿2016/01/26 10:13

C++において、テキストファイルから文字を読み込み、10バイト目の文字を取得するロジックを作っています。

流れとしてはifstreamでファイル開き、getlineで一行ずつ読み込んだ内容を
string型の変数に結合しています。

その結合した文字列の10文字目ではなく10バイト目の文字が知りたいのですが、
どのような手段がありますでしょうか?
ご教授いただければ幸いです。

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

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

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

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

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

cateye

2016/01/26 10:33 編集

「10文字目ではなく10バイト目」言う以上多バイト文字(UTF-8など)が含まれていると思っていいですか?
ryo_se

2016/01/26 10:36

はい、その認識で問題ありません。
guest

回答5

0

C++初心者なので、コーディング方法の拙い点はご容赦ください…

他の回答者さんもご指摘のように、文字コードの種類が決まらないと具体的なアルゴリズムは決められないですよね。
そこで、文字コードがUTF-8であると仮定して、以下のようなコードを書いてみました。

C++

1#--- 入力ファイルの内容 --- 2$ cat test.txt 3testテスト123! 4てすとTEST試験 5$ 6 7#--- 入力ファイルの文字コードはUTF-8 --- 8$ nkf -guess test.txt 9UTF-8 10$ 11 12#--- サンプルコード --- 13$ cat test.cpp 14#include <string> 15#include <fstream> 16#include <iostream> 17 18using namespace std; 19 20int main() { 21 22 ifstream ifs("test.txt"); 23 24 string str, chr; 25 int lines, char_size, s_byte, e_byte; 26 unsigned char lead; 27 28 lines = 0; // 行番号 29 30 // 一行ずつ読み込んで処理 31 while (ifs && getline(ifs, str)) { 32 printf("%3d : %s\n", ++lines, str.c_str()); 33 34 s_byte = 0; // 文字の最初のバイト位置 35 e_byte = 0; // 文字の最後のバイト位置 36 for (string::iterator it = str.begin(); it != str.end(); it += char_size) { 37 38 lead = *it; 39 40 // 文字のバイト数を判定 41 if (lead < 0x80) { 42 char_size = 1; 43 } else if (lead < 0xE0) { 44 char_size = 2; 45 } else if (lead < 0xF0) { 46 char_size = 3; 47 } else { 48 char_size = 4; 49 } 50 51 s_byte = e_byte + 1; 52 e_byte = s_byte + char_size - 1; 53 chr = str.substr(distance(str.begin(), it), char_size); 54 printf(" %2d - %2d : %s\n", s_byte, e_byte, chr.c_str()); 55 56 // 文字の最後のバイト位置が10バイト目以上になったら次の行へ 57 if (e_byte >= 10) 58 break; 59 60 } 61 62 } 63 64} 65$ 66 67#--- 実行結果 --- 68$ ./a.out 69 1 : testテスト123! 70 1 - 1 : t 71 2 - 2 : e 72 3 - 3 : s 73 4 - 4 : t 74 5 - 7 :75 8 - 10 :76 2 : てすとTEST試験 77 1 - 3 :78 4 - 6 :79 7 - 9 :80 10 - 10 : T 81$

色々な文字コードに対応させるには、各種文字コードに対応した「10バイト目の文字を検出する」関数を事前に用意しておき、入力ファイルの文字コードを(何らかの方法で)判定した上で、対応する関数を呼び出せば良いのではないでしょうか。

投稿2016/01/26 20:40

pi-chan

総合スコア5936

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

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

Chironian

2016/01/26 23:52

5, 6バイト文字もありますので、if文を後2つ追加すれば完全になると思います。 そして、このフローならShift-JIS対応も容易ですね。
catsforepaw

2016/01/27 01:09

UTF-8で5バイト以上になったらそれは不正文字として処理すべきです。より厳密さを求めるなら、冗長なコードも不正文字とする処理も必要になります。UTF-8も意外と面倒くさいです。
Chironian

2016/01/27 01:21

あらら、5, 6バイトは規定されているけど、不正なシーケンスとして定義されているのですね。確かにややこしい。 訂正ありがとうです。
catsforepaw

2016/01/27 01:52 編集

というか、Unicodeは21bit(0x10FFFF)までと規定されていますので、UTF-8だと4バイトで足ります。それ以上だと自動的に不正文字ということになります。また、頭に0を補ってビット数を増やすことで冗長にエンコードすることができてしまい、セキュリティ的な問題を引き起こすことがあるのです。
Chironian

2016/01/27 02:07

言い訳なんですが、https://ja.wikipedia.org/wiki/UTF-8 の大きな表だけみて説明を見落としてしまい、5, 6バイトも正規と勘違いしてしまいました。えへへ。
guest

0

前提としてはこういうことですよね。

C++

1/*-------- ファイルの中身 2あいう 3えお 45きくけこ 6----------*/ 7 8string str; 9// ファイルからgetloneで1行ずつ読み込んで連結 10// 結果、strに "あいうえおかきくけこ" が入る

10文字目ではなく10バイト目の文字が知りたいのですが、
どのような手段がありますでしょうか?

C++

1char ch = str[9]; // stringの文字位置は0から始まるので10文字目は[9]

stringクラスは[]演算子をオーバーロードしているので、このように単純に取得したい文字の位置を配列の添え字として指定すれば良いです。
マルチバイト文字云々に関しては、それを判った上でのご質問と解釈します。

投稿2016/01/26 13:29

catsforepaw

総合スコア5938

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

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

ryo_se

2016/01/26 14:15

そんなに単純なんですね。 ただ日本語が入るとcharでの値取得はうまくいかないようですね。
catsforepaw

2016/01/26 14:21

日本語はShift-JISでは2バイト、UTF-8では2~4バイトで表現されるので、文字数とバイト数が一致しないのです。ほかの回答者さんもその辺を気にしていらっしゃいますね。
guest

0

こんにちは。

その結合した文字列の10文字目ではなく10バイト目の文字が知りたいのですが、

他バイト文字の場合、10バイト目が他バイト文字の最初のバイトかもしれないし、最後のバイトかもしれません。ですので、文字コードを決めないとアルゴリズムを決定できないように思います。
もし、UTF-8なら、ここをみればアルゴリズムを作れると思います。

10バイト以上読み込み、10バイト目が含まれる文字を表示するのであれば、使われている文字エンコードの1文字の最大バイト数+9バイト以上をifstreamのread()で読んで10バイト目付近を解析すればできるだろうと思います。

投稿2016/01/26 12:37

Chironian

総合スコア23272

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

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

nob.

2016/01/26 12:49

うーーーん、そんなに難しい事を考えているのか… だとすると、この問題は私では手が出ません。
ryo_se

2016/01/26 14:16

確かにそうですね、マルチバイトで考えると単純に考えるのは難しいようです。
guest

0

対象のテキストファイルから1文字読み取る事は出来ますか?
出来るなら、それを10回繰り返せば良いのではないですか?
バイト単位で考えているのに、なぜ行単位で読み込むのですか?
同じく、string型にするのも理解できません。
ひょっとして、「改行文字はカウントしない」という仕様なのでしょうか?

投稿2016/01/26 12:27

nob.

総合スコア711

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

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

cateye

2016/01/26 12:43 編集

文字数ではなくバイト数という事なので、文字としては読み込めないと思います。 たとえば、"aa"は2バイトですが"ああ"は(シフトJISでは)4バイトあります。 まぁ、unix環境でfgetcあたりならいけるでしょうが・・・質問者の意図が分からないのでなんとも言えませんが。
ryo_se

2016/01/26 13:53

正確に言うとまとめて読み込みたかったのですが、行単位以外で取得する方法が調べてもわかりませんでした(なにぶんC関係の知識があまりないもので・・) 可能であればそちらも教えていただけると助かります。
guest

0

マニピュレータの使い方がよく分かってないのでprintf()にしました^^;

cpp

1~/test/cpptst >./a.out 2a1=ffffffe3, a2=39 3~/test/cpptst >cat tst03.cpp 4#include <iostream> 5#include <string> 6#include <cstdio> 7using namespace std; 8 9int main() 10{ 11 string line1 = "これはテスト用の文字列です"; 12 string line2 = "01234567890123456789"; 13 14 char ans1 = line1.c_str()[9]; 15 char ans2 = line2.c_str()[9]; 16 17 printf("a1=%02x, a2=%02x\n", ans1, ans2); 18 19 return 0; 20} 21~/test/cpptst >

投稿2016/01/26 11:52

編集2016/01/26 11:55
cateye

総合スコア6851

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

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

cateye

2016/01/26 12:10

アクセスする前にはsize()やlength()などで範囲チェックをしてください。
ryo_se

2016/01/26 14:14

やはり日本語が入るとcharでの値取得はうまく取れないのですね (空白になったりするようです)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問