C++における文字列の扱い
C++における文字列の扱いはとてつもなく複雑。 文字セット、型、関数などいろいろあるので調べてみた。
VC++ 2010 Express
2種類の文字コードを扱える。 どの文字コードを扱うかによって、プログラミングで用いる型や関数が変わる。
マルチバイト文字は日本語のWindowsではShift-JISを指す。 おそらく英語圏のWindowsでは別の文字コードを指す。
ソースコードファイルのCharset
Unicode文字セットを使用しても、保存されるソースコードファイルはShift-JISだった。 ちなみにVC#2010ExpressはUTF8(BOM有)だった。
どちらも内部コードとは一致しない。 VC++はUnicode文字セットを選んだらUnicode(UTF16?)のはず。でもソースコードファイルはShift-JIS。 VC#は内部ではUTF16LEらしい。でもソースコードはUTF8。
プログラム上での文字コードとは違う。 なぜ?そういうものなの? 文字セット周りはよくわからない。
型
型名 | byte | Charset(VC++名) | Charset(標準規格名) |
---|---|---|---|
char | 1 | マルチバイト文字セット | Shift-JIS |
wchar_t | 2 | Unicode文字セット(ワイド文字) | Unicode |
- 確保するメモリの量が異なる
- エンコーディングが異なる
- Shift-JIS
- UTF16
プログラミングの型
- char
- char [ ]
- char*
- const char
- const char [ ]
- const char*
wchar.h
- wchar_t
- wchar_t [ ]
- wchar_t*
- const wchar_t
- const wchar_t [ ]
- const wchar_t*
string.h
- string
- wstring
- base_string
class型。
tchar.h
typedef wchar_t TCHAR;
#define __T(x) L ## x
TCHARは、Unicode文字セットを使うときは「wchar_t」になる。 マルチバイト文字セットを使うときは「char」になる。
_T()は、Unicode文字セットを使うとき、「L"文字列"」とする。
マルチバイト文字セットのときは「"文字列"」とする。
ワイド文字のリテラルを意味するとき、「L""」とするらしい。
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1397301393
http://wlog.flatlib.jp/item/1086
windows.h
Windows | standard |
---|---|
LPSTR | char* |
LPWSTR | wchar_t* |
LPTSTR | TCHAR* |
LPCSTR | const char* |
LPCWSTR | const wchar_t* |
LPCTSTR | const TCHAR* |
これが紛らわしい。LPとか要る?。ポインタ型ならをつければいい。 Lの有無は16/32bitだと思う。変数ごとに区別する必要ある? コンパイルするときにスイッチして定義を切り替えればいいだけでは?64bitのときはどうするの?128bitは? constも必要ならつければいい。 STRもcharでいいし、WSTRもwchar_t*で足りる。 ただ、マルチバイトかUnicodeだけスイッチできればいい。 TCHARだけでいい。Unicodeに統一すればTCHARもいらない。 多すぎて無駄に見づらいしわかりづらい。嫌がらせかと思った。
http://blog.systemjp.net/entry/20090519/p3
http://www.wisdomsoft.jp/421.html
https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
文字列の扱い
未定義 | コンパイル時切替 | Unicode | マルチバイト |
---|---|---|---|
char | TCHAR | wchar_t | char |
関数
http://www.t-net.ne.jp/~cyfis/c/string/TCHAR.html
http://palepoli.skr.jp/os/tchar.php
文字列を扱う関数だけでも膨大な数である。
- 機能
- 文字セット
- 長さ指定
- バッファ・オーバー・ラン対策
- ロケール指定
長さを取得したり、比較したり、数値に変換したり、さまざまな機能の関数がある。 さらにそれらは、使用する文字セットによって別々の関数である。
他にも、長さ指定をする関数としない関数、バッファ・オーバー・ラン対策する関数としない関数、ロケール指定する関数としない関数がある。 このように、同じ機能の関数でも細かい違いがある。 それぞれが個別に定義された関数なので、膨大な数の関数がある。
これらの中には推奨された関数、推奨されない関数がある。 その理由から使用する関数を選ぶ。
さらにカプセル化されたclassが使えると楽。 でも、TCHARに対応したstring型がない。 WindowsAPIがstring型でなくTCHAR。 なので結局はTCHARを操ることになる。
文字列
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
文字数の取得 | strlen | _tcslen | |_tcscnlen_l |
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
比較(大小文字区別) | strcmp | _tcscmp | _tcsncmp |
比較(大小文字無視) | _stricmp | _tcsicmp | _tcsnicmp_l |
比較(現在のロケールまたは指定された LC_COLLATE 変換状態カテゴリを使用) | strcoll | _tcscoll |
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
検索(指定した文字が最初に見つかったindexを返す) | strchr | _tcschr | |
検索(指定した文字が最初に見つかった文字へのポインタを返す) | strpbrk | _tcspbrk | |
検索(指定した文字が最後に見つかった文字へのポインタを返す) | strrchr | _tcsrchr | |
検索(指定した文字に含まれない最初の文字へのindexを返す) | strspn | _tcsspn | |
検索(指定した文字列が最初に見つかった文字列へのポインターを返す) | strstr | _tcsstr | |
検索(文字列内の次のトークンへのポインタを返す) | strtok | _tcstok |
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
変換(文字列→整数(int)) | atoi | _ttoi | |
変換(文字列→整数(long)) | strtol | _ttol | |
変換(文字列→整数(__int64)) | atoi64 | _ttoi64 | |
変換(文字列→整数(unsigned __int64)) | atoui64 | _ttoui64 | |
変換(文字列→少数(float)) | atof | _ttof | |
変換(文字列→少数(double)) | strtod | _ttod |
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
変換(整数(int)→文字列) | itoa | _itot | _itot_s |
変換(整数(long)→文字列) | ltoa | _ttol | _ltot_s |
変換(整数(__int64)→文字列) | i64toa | _i64tot | _i64tot_s |
変換(整数(unsigned __int64)→文字列) | ui64toa | _ui64tot | _ui64tot_s |
変換(少数(float)→文字列) | ftoa | _ftot | _ftot_s |
変換(少数(double)→文字列) | dtostr | _dtot | _dtot_s |
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
変換(大文字→小文字) | _strlwr | _tcslwr | |
変換(小文字→大文字) | _strupr | _tcsupr |
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
複製(別途メモリ確保) | _strdup | _tcsdup |
入出力
入出力関数は、入出力の対象によって関数名の接頭辞が変わる。
対象 | 接頭辞 |
---|---|
標準入力ストリーム | (無印) |
バッファ | s~ |
コンソール | c~ |
ファイル | f~ |
対象 | printf |
---|---|
標準入力ストリーム | printf |
バッファ | sprintf |
コンソール | cprintf |
ファイル | fprintf |
名前の規則性
接頭辞 | 略? | 対象 |
---|---|---|
(無印) | standard | 標準ストリーム |
c | console | コンソール |
s | stream | ストリーム。バッファ(プログラマが指定したメモリ) |
f | file | ファイル(プログラマが指定したファイル) |
標準ストリームとコンソールの違いがよくわからない。 標準ストリームはあらかじめ用意されたストリーム。ストリームはメモリ。だと思う。
- ストリームとは、ユーザ・プロセスとデバイス間の全二重結合のことである
- ストリームとは、プロセスとその実行環境(通常は端末)の間であらかじめ確立されている入出力チャネルである
http://oshiete.goo.ne.jp/qa/4821435.html
https://ja.wikipedia.org/wiki/%E6%A8%99%E6%BA%96%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0
コンソールはMS-DOSプロンプト(コマンドプロンプト・端末・ターミナル)? ストリームの内容をファイルに保存したもの?
標準ストリーム(無印)とコンソール(c)はどちらもプログラマが用意する必要がなく、あらかじめ用意されたメモリ領域やファイルだと思う。 対して、ストリーム(s)とファイル(f)は、プログラマが自分で用意したメモリやファイルを対象としたものだと思う。
おそらく、以下の略だと思う。
バッファ入出力
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
コピー(先頭から上書き) | strcpy | _tcscpy | _tcsncpy_s_l |
結合(末尾に追加) | strcat | _tcsncat | _tcsnccat_s_l |
書式付出力 | sprintf | _stprintf_s | _sntprintf_s_l |
書式付出力 | sscanf | _stscanf_s | _sntscanf_s_l |
標準入出力
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
書式付出力 | printf | _tprintf | _printf_s_l |
書式付入力 | scanf | _tscanf | |_scanf_s_l |
一文字出力(エコー有) | putchar | ||
一文字入力(エコー有) | getchar | ||
一文字出力(エコー無) | putch | ||
一文字入力(エコー無) | getch | ||
1行出力 | puts | _putts | |
1行入力 | gets | _getts | _fgetts |
標準入出力は以下の3種類がある。
- stdin
- stdout
- stderr
おそらく以下の略。
- standerd input
- standerd output
- standerd error
ファイル入出力
内容 | 未定義 | コンパイル時切替 | 推奨(長さ指定+バッファ・オーバー・ラン対策+ロケール指定) |
---|---|---|---|
書式付出力 | fprintf | _ftprintf_s | _ftprintf_s_l |
書式付入力 | fscanf | _ftscanf_s | _ftscanf_s_l |
一文字出力 | fputc | ||
一文字入力 | fgetc | ||
1行出力 | fputs | _fputts | |
1行入力 | fgets | _fgetts |
定型処理
// メモリ確保
int size = 100;
TCHAR* pStr = malloc(sizeof(TCHAR) * size);
// 初期化
memset(pStr, '\0', size);
// メモリ解放
free(pStr);
pStr = NULL;
解放し忘れ防止
if (NULL != pStr) { free(pStr); pStr = NULL; }
pStr = malloc(sizeof(TCHAR) * size);
多重解放防止
if (NULL != pStr) { free(pStr); pStr = NULL; }
base_string
#include <stdio.h>
#include <tchar.h>
#include <string>
typedef std::basic_string<TCHAR> tstring;
tstring str1 = _T("abc");
tstring str2 = _T("DEF");
tstring str3 = str1 + str2;
OutputDebugString(strC.c_str());
ロケール指定
_tsetlocale(LC_ALL, _T("")); // OSのデフォルト値(日本語WindowsではShift-JIS(CP932))
setlocale( LC_ALL, ".ACP" ); // OSのデフォルト値(日本語WindowsではShift-JIS(CP932))
http://99blues.dyndns.org/blog/2010/07/stl_locale/
他
文字列の操作以外にも、TCHARと関わる関数があった。
Execute functions
機能 | 関数 |
---|---|
新しい子プロセスを読み込んで実行します | _texecl |
新しいプロセスを作成して実行します | _tspawnl |
コマンドを実行します | _tsystem |
Time functions
機能 | 関数 |
---|---|
日時を文字列へ変換 | _tctime64_s |
日時を文字列へ変換 | _tutime64_s |
日付を文字列へ変換 | _tstrdate_s |
Directory functions
機能 | 関数 |
---|---|
現在の作業ディレクトリを変更します | _tchdir |
現在の作業ディレクトリを取得します | _tgetcwd |
指定されたドライブの現在の作業ディレクトリの完全なパスを取得します | _getdcwd |
新しいディレクトリを作成します | _tmkdir |
ディレクトリを削除します | _trmdir |
略 | 全? |
---|---|
chdir | change directory |
getcwd | get console working directory |
getdcwd | drive change working directory |
mkdir | make directory |
rmdir | remove directory |
Environment/Path functions
機能 | 関数 |
---|---|
指定した相対パス名には、絶対パスでも完全パス名を作成します | _tfullpath |
パス名の要素からパス名を作成します | _tmakepath_s |
パス名を構成要素に分解します | _tsplitpath_s |
機能 | 関数 |
---|---|
cmd.exeで実行できるコマンドの完全パスを返す | _tpgmptr |
cmd.exeで実行できるコマンドの完全パスを返す | _get_tpgmptr |
機能 | 関数 |
---|---|
環境変数から指定した変数名の値を返す | _tdupenv_s |
環境変数の追加、削除、変更 | _putenv_s |
環境のパスを使用してファイルを検索します | _tsearchenv_s |
Io functions
機能 | 関数 |
---|---|
ファイルのアクセス許可を設定する | _taccess_s |
ファイルのアクセス許可の設定を変更します | _tchmod |
新しいファイルを作成します | _tcreat |
ファイル検索(指定したファイル名に最初に一致した検索ハンドルを返す) | _tfindfirst |
ファイル検索(次の検索ハンドルを返す) | _tfindnext |
一意のファイル名を作成します | _tmktemp_s |
ファイルを開きます | _topen |
ファイルを削除します | _tremove |
ファイルまたはディレクトリの名前を変更します。 | _trename |
ファイルのステータス情報を取得します | _tstat |
Setlocale functions
機能 | 関数 |
---|---|
実行時ロケールを設定または取得します | _tsetlocale |
名前の疑問
なぜstrtoiでなくatoiなのか。aはASCIIかANSIの略だろうけどstrtoiでないと他の関数と整合性がとれていない。
t,tc,ts,tcs,などの違いは何か。よくわからない。 translate, translate character, translate string, translate character set の略なのだろうか。 w,wc,ws,wcs,も同様。wはwideか。
整数型もわかりづらい。
byte, short, int, long, long long, とかわかりづらい。
もう全部bit数を直接かいて型表現したい。
int8, int16, int32, int64, int128, int256, ... みたいに。
もしくはintとだけ書いてコンパイル時に32/64bitなど任意に切りかえれるようにするとか。
https://msdn.microsoft.com/ja-jp/library/s3f49ktz.aspx
https://msdn.microsoft.com/en-us/library/29dh1w7z.aspx