コンテンツにスキップ

第 7 回 | 2024-11-15

  • 文章を変数で扱う方法を学ぶ
  • 文字を扱う方法を学ぶ
  • 文字の内部表現が整数であることを学ぶ
  • 文字列の個別要素にアクセスする方法を学ぶ

1. 文章を変数で扱う

  • 文章(プログラミング用語では「文字列」)を変数で扱うには、std::string 型の変数を使う。
  • std::string 型は、コードの先頭に #include <string> を書くことで使えるようになる。
#include <iostream>
#include <string>
  • std::string 型の変数は、intdouble と同じように、次の操作ができる。
    • = で代入する
    • std::cout で出力する
    • std::cin で入力する
    • ==!= で比較する

1.1 文章をあつかう (1)

  • std::string 型の変数にキーボードから文字列を入力し、それが特定の文字列と一致するかどうかを調べるプログラムの例を示す。

📙 Unit 24
#include <iostream>
#include <string>

int main()
{
	std::cout << "お前さん、お酒を飲める年齢かい?";

	std::string answer;
	std::cin >> answer;

	if (answer == "yes")
	{
		std::cout << "うーん、そうは見えないけどね\n";
	}
	else
	{
		std::cout << "ここは子どもが来る店じゃないよ\n";
	}
}
入力
yes
出力
うーん、そうは見えないけどね

1.2 文章をあつかう (2)

  • std::string 型の変数への入力では、intdouble 型への入力と同様に、半角空白が区切りと扱われてしまう。

int a, b;
std::cin >> a >> b;
入力
10 20

  • それを避けるための方法として、std::getline(std::cin, ...) がある。... の部分には、入力された文字列を格納する std::string 型の変数を指定する。

半角空白で入力が区切られる例

  • この例では、std::cin で入力された文字列が、半角空白で区切られてしまう。
  • townName には「Castle」だけが格納される。

#include <iostream>
#include <string>

int main()
{
	std::string townName;
	std::cin >> townName; // Castle Town と入力してみる

	std::cout << "ここは " << townName << " だ\n";
}
入力
Castle Town
出力
ここは Castle だ

1 行全体を入力する例

  • 次の例では、std::getline(std::cin, townName) を使う。
  • 半角空白を含む 1 行全体を townName に格納できる。

#include <iostream>
#include <string>

int main()
{
	std::string townName;
	std::getline(std::cin, townName); // Castle Town と入力してみる

	std::cout << "ここは " << townName << " だ\n";
}
入力
Castle Town
出力
ここは Castle Town だ

1.3 文章をあつかう (3)

  • 対話型のプログラムでユーザの情報を聞き取り、それを使って挨拶をするプログラムの例を示す。
#include <iostream>
#include <string>

int main()
{
	std::string name;
	std::cout << "お名前は何ですか?\n";
	std::getline(std::cin, name);
	std::cout << "こんにちは、" << name << "さん!\n";

	std::string occupation;
	std::cout << "職業は何ですか?\n";
	std::getline(std::cin, occupation);
	std::cout << "あなたは" << occupation << "ですね!\n";

	std::string city;
	std::cout << "お住まいはどちらですか?\n";
	std::getline(std::cin, city);
	std::cout << city << "にお住まいなんですね!\n";

	std::cout << "あなたは" << name << "さんで、職業は" << occupation << "、" << city << "にお住まいですね!\n";
	std::cout << "よろしくお願いします!\n";
}

2. 文字を扱う

  • 文字列は複数の文字から構成される。この章では文字を扱う方法を学ぶ。

2.1 文字

  • アルファベット、数字、記号などの 1 文字は、文字列ではなく単一の文字データとして扱うことができる。
  • 文字を表すには、シングルクォーテーション ' で囲む(例: 'A', '1', '$')。
  • この形式で扱える文字は、ASCII コード表 に含まれる文字のみ。
日本語は文字単位で扱うのが難しい

ASCII コード表に含まれない文字(平仮名や漢字、全角記号など)を文字単位で扱うには特殊な方法が必要なため、本講義では扱わない。本講義で「文字単位」の処理を扱うときは、ASCII コード表 に含まれる文字を前提とする。

#include <iostream>

int main()
{
	'a';

	'A';

	'5';

	'-';

	' ';

	'\n';
}

2.2 文字の型

  • 文字は char 型の変数で扱う。
#include <iostream>

int main()
{
	char c1 = 'a';

	char c2 = 'A';

	c1 = '-';

	c2 = '!';
}

2.3 文字の出力

  • char 型の値を std::cout に送ると、それが表現する文字が出力される。

#include <iostream>

int main()
{
	std::cout << 's';

	std::cout << 'o' << 'p';

	char c1 = 'h';

	char c2 = 'i';

	char c3 = 'a';

	char c4 = '\n';

	std::cout << c1 << c2 << c3 << c4;
}
出力
sophia

2.4 改行文字

  • これまで、改行を出力する際に "\n" を使ってきたが、'\n' でも同じことができる。
  • '\n' の方がコンパクトなので、今後はこちらを使うほうが好ましい。

#include <iostream>

int main()
{
	std::cout << 123 << "\n";

	std::cout << 456 << '\n'; // こちらを使う方が好ましい
}
出力
123
456

2.5 文字の入力

  • char 型の変数への std::cin により、キーボードからの入力 1 文字を変数に格納できる。

#include <iostream>

int main()
{
	char ch;

	std::cin >> ch;

	std::cout << "Input: " << ch << '\n';
}
入力
w
出力
Input: w

3. 文字が整数であることを知る

  • 文字は内部的には整数として扱われる。例えば、文字 'A' は数値 65 に、改行文字 '\n'10 に対応する。
  • 文字と整数の対応は、ASCII コード表 によって定義される。

3.1 文字の実体は整数

  • 文字の実体は 0 から 127 の整数であり、std::cout に文字を送るとき、実際に送られているのはその整数である。
  • ただし、std::cout は、その整数が char 型である場合、整数を ASCII コード表に基づいて文字に変換して出力する。

#include <iostream>

int main()
{
	char ch = 'A'; // 65

	std::cout << ch << '\n'; // char 型の 65 を送っている

	ch = 66;

	std::cout << ch << '\n';

	ch = 33;

	std::cout << ch << '\n';
}
出力
A
B
!

3.2 文字の計算 (1)

  • 文字は整数であるため、足し算や引き算をすることができる。
  • どの整数がどの文字に対応するかは ASCII コード表 を参照する。
    • char 型と int 型の計算の結果は int 型になってしまう点には注意。文字として出力する場合は、char 型にする。

#include <iostream>

int main()
{
	char ch1 = 'A'; // 65

	char ch2 = (ch1 + 10); // 75

	std::cout << ch2 << '\n';
}
出力
K

3.3 文字の計算 (2)

#include <iostream>

int main()
{
	for (int i = 0; i < 10; ++i)
	{
		char ch = ('A' + i);

		std::cout << ch;
	}

	std::cout << '\n';
}
出力
ABCDEFGHIJ

3.4 文字の計算 (3)

  • 大文字のアルファベットに 32 を足すと小文字になる。
  • 小文字のアルファベットから 32 を引くと大文字になる。

#include <iostream>

int main()
{
	char ch1 = 'A';

	char ch2 = (ch1 + 32); // 小文字に変換する

	std::cout << ch1 << ch2 << '\n';

	char ch3 = 'x';

	char ch4 = (ch3 - 32); // 大文字に変換する

	std::cout << ch3 << ch4 << '\n';
}
出力
Aa
xX

char と int の計算結果は int

char 型と int 型の値の計算の結果は int 型であるため、次のように書くと、文字ではなく整数が出力される。

#include <iostream>

int main()
{
	char ch1 = 'A';

	std::cout << (ch1 + 32) << '\n'; // 足し算の結果は char ではなく int
}

3.5 文字の計算 (4)

  • 入力された 1 文字を大文字にして(それ以外の場合はそのまま)出力するプログラムは次のように書ける。

#include <iostream>

int main()
{
	char ch;

	std::cin >> ch;

	if (('a' <= ch) && (ch <= 'z')) // 小文字の場合
	{
		char upper = (ch - 32);

		std::cout << upper << '\n';
	}
	else // それ以外の場合
	{
		std::cout << ch << '\n';
	}
}
入力
t
出力
T

'0' は 0 ではない

3.1 で学んだとおり、'0' という文字の実体は 48 であり、0 ではない。見た目の数字と ASCII コード表の数値を混同した次のコードは正しく動作しない。

間違っているコード
#include <iostream>

int main()
{
	char ch = '3'; // 'A' なども試してみよう

	if ((0 <= ch) && (ch <= 9))
	{
		std::cout << ch << " is a digit\n";
	}
	else
	{
		std::cout << ch << " is not a digit\n";
	}
}
正しいコード
#include <iostream>

int main()
{
	char ch = '3'; // 'A' なども試してみよう

	if (('0' <= ch) && (ch <= '9'))
	{
		std::cout << ch << " is a digit\n";
	}
	else
	{
		std::cout << ch << " is not a digit\n";
	}
}

4. 文字列に対する操作

4.1 文字列内の 1 文字にアクセスする

  • std::string は連続する複数(0 個以上)の char 型の要素から構成される。
  • std::string 型の変数は、[index] を使って、その中の 1 つの要素にアクセスできる。index は 0 から始まり、要素数 - 1 まである。
  • 存在しない要素にアクセス(要素数が 6 なのに s[10])すると、実行時エラーが発生する。存在しない要素には絶対にアクセスしてはいけない。

#include <iostream>
#include <string>

int main()
{
	std::string s = "sophia"; // 要素数 6 の文字列

	std::cout << s[0] << '\n';

	std::cout << s[1] << '\n';

	char ch = s[5];

	std::cout << ch << '\n';
}
出力
s
o
a

4.2 文字列内の 1 文字を変更する

  • [index] 経由で、文字列内の指定した要素を変更できる。

#include <iostream>
#include <string>

int main()
{
	std::string s = "sophia"; // 要素数 6 の文字列

	s[0] = 'S';

	s[5] = 'e';

	std::cout << s << '\n';
}
出力
Sophie

4.3 文字列の長さを調べる

  • std::string 型の文字列がいくつの要素を持っているか(文字数)は、変数名に続いて .size() を書くことで調べることができる。
  • .size() は、その文字列の要素数を整数で返す。
  • .size() が返す値の型は、厳密には int 型ではなく size_t(負の値を持たない整数)型だが、おおよそ int 型と同じように扱って問題ない。

#include <iostream>
#include <string>

int main()
{
	std::string s = "sophia";

	int n = s.size();

	std::cout << n << '\n';
}
出力
6

  • より正確に書く場合は、size_t 型を使う。

#include <iostream>
#include <string>

int main()
{
	std::string s = "sophia";

	size_t n = s.size();

	std::cout << n << '\n';
}
出力
6

4.4 先頭の要素と末尾の要素

  • 1 要素以上ある文字列について、先頭の要素は s[0]、末尾の要素は s[s.size() - 1] である(自明)。

#include <iostream>
#include <string>

int main()
{
	std::string s = "sophia";

	std::cout << s[0] << '\n';

	std::cout << s[s.size() - 1] << '\n';
}
出力
s
a

4.5 ループで各文字にアクセスする

  • for とを組み合わせることで、文字列の各要素にアクセスすることができる。
  • [index]index が範囲外にならないよう、ループ回数が s.size() を超えないように注意する。
  • for (size_t i = 0; i < s.size(); ++i) のように書けば問題ない。

#include <iostream>
#include <string>

int main()
{
	std::string s = "sophia";

	for (size_t i = 0; i < s.size(); ++i)
	{
		std::cout << i << ": " << s[i] << '\n';
	}
}
出力
0: s
1: o
2: p
3: h
4: i
5: a

✅ 振り返りチェックリスト

  • 文章を変数で扱う方法を学んだ
  • 文字を扱う方法を学んだ
  • 文字の内部表現が整数であることを学んだ
  • 文字列の個別要素にアクセスする方法を学んだ

課題

🐣 練習問題

入力された各文字列の中に a という文字がいくつあるかを数えるプログラムを作成してください。

文字列の個数と、それぞれの文字列が次の形式で標準入力から与えられます。文字列は 1 文字以上 100 文字以下で、半角空白は含まれません。

入力の形式
n
s_1
s_2
...
s_n
入力データ 意味
n 文字列の個数
s_1 1 番目の文字列
s_2 2 番目の文字列
s_n n 番目の文字列

それぞれの文字列に含まれる a の個数を改行で区切って出力してください。

入力例

入力
5
apple
Bird
CAT
Hawaii
AaaAaa
出力
1
0
0
2
4

  • apple には a が 1 文字含まれています。
  • Bird には a が含まれていません。
  • CAT には a が含まれていません。
  • Hawaii には a が 2 文字含まれています。
  • AaaAaa には a が 4 文字含まれています。

解法 Step 1

  • 指定された回数だけ文字列の入力を受け取って出力するだけのプログラムを作成します。
  • 問題文より、半角空白は含まれないため、std::cin >> s; で文字列を受け取れます。

#include <iostream>
#include <string>

int main()
{
	// 文字列の個数を受け取る
	int n;
	std::cin >> n;

	for (int i = 0; i < n; ++i)
	{
		// 文字列を 1 個受け取る
		std::string s;
		std::cin >> s;

		std::cout << "Input: " << s << '\n';
	}
}
入力
5
apple
Bird
CAT
Hawaii
AaaAaa
出力
Input: apple
Input: Bird
Input: CAT
Input: Hawaii
Input: AaaAaa

解法 Step 2

  • 文字列の各要素にアクセスするプログラムを追加します。
  • すでに使われている変数 i と重複しないよう、k という変数を使います。

#include <iostream>
#include <string>

int main()
{
	// 文字列の個数を受け取る
	int n;
	std::cin >> n;

	for (int i = 0; i < n; ++i)
	{
		// 文字列を 1 個受け取る
		std::string s;
		std::cin >> s;

		std::cout << "Input: " << s << '\n';

		for (size_t k = 0; k < s.size(); ++k)
		{
			char ch = s[k];

			std::cout << ch << '\n';
		}
	}
}
入力
5
apple
Bird
CAT
Hawaii
AaaAaa
出力
Input: apple
a
p
p
l
e
Input: Bird
B
i
r
d
Input: CAT
C
A
T
Input: Hawaii
H
a
w
a
i
i
Input: AaaAaa
A
a
a
A
a
a

解法 Step 3

  • 文字 a の個数を数えて出力します。
  • ch は文字なので、ch == "a" ではなく ch == 'a' であることに注意します。

#include <iostream>
#include <string>

int main()
{
	// 文字列の個数を受け取る
	int n;
	std::cin >> n;

	for (int i = 0; i < n; ++i)
	{
		// 文字列を 1 個受け取る
		std::string s;
		std::cin >> s;

		// 文字列の中に含まれる 'a' の個数
		int count = 0;

		for (size_t k = 0; k < s.size(); ++k)
		{
			char ch = s[k];

			if (ch == 'a')
			{
				++count;
			}
		}

		std::cout << count << '\n';
	}
}
入力
5
apple
Bird
CAT
Hawaii
AaaAaa
出力
1
0
0
2
4

📝 提出課題

入力された各文字列の中に含まれる「大文字のアルファベット」「小文字のアルファベット」「数字」「それ以外の文字」をそれぞれ数えるプログラムを作成してください。

文字列の個数と、それぞれの文字列が次の形式で標準入力から与えられます。文字列は 1 文字以上 100 文字以下で、半角空白は含まれません。

入力の形式
n
s_1
s_2
...
s_n
入力データ 意味
n 文字列の個数
s_1 1 番目の文字列
s_2 2 番目の文字列
s_n n 番目の文字列

それぞれの文字列に含まれる「大文字のアルファベットの個数」「小文字のアルファベットの個数」「数字の個数」「それ以外の文字の個数」を半角空白で区切って出力し、各文字列の結果を改行で区切って出力してください。

大文字のアルファベットの個数 小文字のアルファベットの個数 数字の個数 それ以外の文字の個数
入力例 1

入力
2
Apple123
Sophia???
出力
1 4 3 0
1 5 0 3

  • Apple123 には大文字のアルファベットが 1 文字、小文字のアルファベットが 4 文字、数字が 3 文字、それ以外の文字が 0 文字含まれています。
  • Sophia??? には大文字のアルファベットが 1 文字、小文字のアルファベットが 5 文字、数字が 0 文字、それ以外の文字が 3 文字含まれています。
入力例 2

入力
3
\(^o^)/
1234567890
ABCabc
出力
0 1 0 6
0 0 10 0
3 3 0 0

  • \(^o^)/ には大文字のアルファベットが 0 文字、小文字のアルファベットが 1 文字、数字が 0 文字、それ以外の文字が 6 文字含まれています。
  • 1234567890 には大文字のアルファベットが 0 文字、小文字のアルファベットが 0 文字、数字が 10 文字、それ以外の文字が 0 文字含まれています。
  • ABCabc には大文字のアルファベットが 3 文字、小文字のアルファベットが 3 文字、数字が 0 文字、それ以外の文字が 0 文字含まれています。