やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

C++における文字列の扱い

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

プログラミングの型

  • 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

文字列の扱い

  • マルチバイト(ASCII,Shift-JIS等)とUnicodeの文字セットのうち、どちらかを選ぶ
  • 文字セットに応じた型、関数、classを用いる
  • 可変長のためメモリ量の指定と確保が必要
未定義 コンパイル時切替 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