第 7 回 | 2024-11-15¶
- 文章を変数で扱う方法を学ぶ
- 文字を扱う方法を学ぶ
- 文字の内部表現が整数であることを学ぶ
- 文字列の個別要素にアクセスする方法を学ぶ
1. 文章を変数で扱う¶
- 文章(プログラミング用語では「文字列」)を変数で扱うには、
std::string
型の変数を使う。 std::string
型は、コードの先頭に#include <string>
を書くことで使えるようになる。
std::string
型の変数は、int
やdouble
と同じように、次の操作ができる。=
で代入する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";
}
}
1.2 文章をあつかう (2)¶
std::string
型の変数への入力では、int
やdouble
型への入力と同様に、半角空白が区切りと扱われてしまう。
- それを避けるための方法として、
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";
}
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";
}
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 コード表 に含まれる文字を前提とする。
2.2 文字の型¶
- 文字は
char
型の変数で扱う。
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;
}
2.4 改行文字¶
- これまで、改行を出力する際に
"\n"
を使ってきたが、'\n'
でも同じことができる。 '\n'
の方がコンパクトなので、今後はこちらを使うほうが好ましい。
#include <iostream>
int main()
{
std::cout << 123 << "\n";
std::cout << 456 << '\n'; // こちらを使う方が好ましい
}
2.5 文字の入力¶
char
型の変数へのstd::cin
により、キーボードからの入力 1 文字を変数に格納できる。
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';
}
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';
}
3.3 文字の計算 (2)¶
#include <iostream>
int main()
{
for (int i = 0; i < 10; ++i)
{
char ch = ('A' + i);
std::cout << ch;
}
std::cout << '\n';
}
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';
}
char と int の計算結果は int
char
型と int
型の値の計算の結果は 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';
}
}
'0' は 0 ではない
3.1 で学んだとおり、'0'
という文字の実体は 48 であり、0 ではない。見た目の数字と ASCII コード表の数値を混同した次のコードは正しく動作しない。
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';
}
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';
}
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';
}
- より正確に書く場合は、
size_t
型を使う。
#include <iostream>
#include <string>
int main()
{
std::string s = "sophia";
size_t n = s.size();
std::cout << n << '\n';
}
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';
}
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';
}
}
✅ 振り返りチェックリスト¶
- 文章を変数で扱う方法を学んだ
- 文字を扱う方法を学んだ
- 文字の内部表現が整数であることを学んだ
- 文字列の個別要素にアクセスする方法を学んだ
課題¶
🐣 練習問題¶
入力された各文字列の中に a
という文字がいくつあるかを数えるプログラムを作成してください。
文字列の個数と、それぞれの文字列が次の形式で標準入力から与えられます。文字列は 1 文字以上 100 文字以下で、半角空白は含まれません。
入力データ | 意味 |
---|---|
n |
文字列の個数 |
s_1 |
1 番目の文字列 |
s_2 |
2 番目の文字列 |
s_n |
n 番目の文字列 |
それぞれの文字列に含まれる a
の個数を改行で区切って出力してください。
入力例
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';
}
}
解法 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';
}
}
}
出力
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';
}
}
📝 提出課題¶
入力された各文字列の中に含まれる「大文字のアルファベット」「小文字のアルファベット」「数字」「それ以外の文字」をそれぞれ数えるプログラムを作成してください。
文字列の個数と、それぞれの文字列が次の形式で標準入力から与えられます。文字列は 1 文字以上 100 文字以下で、半角空白は含まれません。
入力データ | 意味 |
---|---|
n |
文字列の個数 |
s_1 |
1 番目の文字列 |
s_2 |
2 番目の文字列 |
s_n |
n 番目の文字列 |
それぞれの文字列に含まれる「大文字のアルファベットの個数」「小文字のアルファベットの個数」「数字の個数」「それ以外の文字の個数」を半角空白で区切って出力し、各文字列の結果を改行で区切って出力してください。
入力例 1
Apple123
には大文字のアルファベットが 1 文字、小文字のアルファベットが 4 文字、数字が 3 文字、それ以外の文字が 0 文字含まれています。Sophia???
には大文字のアルファベットが 1 文字、小文字のアルファベットが 5 文字、数字が 0 文字、それ以外の文字が 3 文字含まれています。