C 文字列操作 新しいページはコチラ
提供: yonewiki
(→文字列操作) |
(→文字列ファイルへの入出力) |
||
4,432行: | 4,432行: | ||
=='''文字列ファイルへの入出力'''== | =='''文字列ファイルへの入出力'''== | ||
ここまで、やってきた手法で文字列をファイルに保存することをやってみます。文字列はかならずしもテキストファイルとして保管されるものではありません。DBに格納されるもの、バイナリファイルに格納されるもの、圧縮されるもの、様々です。ここでは一般的なテキストファイルとして保管し、簡単なテキストDBとしての操作について作業します。 | ここまで、やってきた手法で文字列をファイルに保存することをやってみます。文字列はかならずしもテキストファイルとして保管されるものではありません。DBに格納されるもの、バイナリファイルに格納されるもの、圧縮されるもの、様々です。ここでは一般的なテキストファイルとして保管し、簡単なテキストDBとしての操作について作業します。 | ||
+ | |||
+ | |||
+ | まずは、テキストから文字列の読み込みのサンプルをぺたぺたとしたいのですが、一応、標準の関数でサポートされている範囲の文字コードはShiftJISとUTF-8とUTF-16でVisualStudio2005からfopenやwfopen関数が拡張されて、2番目の引数ファイルオープンモード文字列に ccs=UTF-8とかccs=UTF-16LEと記載することで、ファイルポインタをつかってのストリーム入力時fgetws関数でワイド文字(UNICODE)に変換してくれます。テキストは自分で作るといいのですが、自分は以下のような簡単なテキストを文字コード別に準備したので、参考にリンクを張っておきます。なんてことないテキストファイルなので、無価値なのものです。 | ||
+ | |||
+ | |||
+ | JISやEUCといったサポートされていない文字コードのテキストファイルの場合はバイナリモードでファイルを読み込む必要があり、1バイトづつ処理をして、文字列として変換したりすることで、文字列操作を行います。変換そのものは先に紹介した文字コード変換で対応することが可能ですので、とりこんだバイナリコードをどうやって変換関数のなかに収めるかというのが課題になると思います。その例はのちほど考えるとして、まずはオーソドックスなファイルの読み込みからやってみます。 | ||
+ | |||
+ | <syntaxhighlight lang="cpp" line start="1"> | ||
+ | #pragma once | ||
+ | #include "stdafx.h" | ||
+ | #include <stdlib.h> | ||
+ | #include <string> //std::string型定義 | ||
+ | #include <mbstring.h> //mbs***関数 | ||
+ | #include <iostream> //cpp cout etc 一般入出力関数 | ||
+ | #include <locale> //Locale関数 | ||
+ | #include <tchar.h> //TCHAR型+_tcs***関数 | ||
+ | #include "atlstr.h" //CString,CStringA,CStringWおよびLPTSTR,LPWSTR,LPSTR,LPCTSTR,LPCWSTR,LPCSTRの定義プリプロセッサ | ||
+ | #include "atlbase.h" //同上 | ||
+ | #include "cstringt.h" //CStringTの定義プリプロセッサ | ||
+ | #include <vector> | ||
+ | |||
+ | #include <sys/timeb.h> //Timeb型構造体 | ||
+ | |||
+ | //_bstr_t型を利用するプリプロセッサ | ||
+ | #include "comutil.h" | ||
+ | #ifdef _DEBUG | ||
+ | #pragma comment(lib, "comsuppwd.lib") | ||
+ | #else | ||
+ | #pragma comment(lib, "comsuppw.lib") | ||
+ | #endif | ||
+ | //_bstr_t | ||
+ | |||
+ | //ICU ucnvプリプロセッサ | ||
+ | #include <unicode/ucnv.h> | ||
+ | #include <unicode/translit.h> | ||
+ | #include <unicode/regex.h> | ||
+ | #ifdef _DEBUG | ||
+ | //#pragma comment(lib, "icudt.lib") | ||
+ | #pragma comment(lib, "icuind.lib")//ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も | ||
+ | #pragma comment(lib, "icuucd.lib")//ICU ucnvを使うために必要なライブラリ | ||
+ | //#pragma comment(lib, "icuiod.lib") | ||
+ | #else | ||
+ | #pragma comment(lib, "icudt.lib") | ||
+ | #pragma comment(lib, "icuind.lib") | ||
+ | #pragma comment(lib, "icuuc.lib") | ||
+ | #pragma comment(lib, "icuio.lib") | ||
+ | #endif | ||
+ | |||
+ | |||
+ | #include "UnicodeConverter.h" | ||
+ | |||
+ | |||
+ | using namespace std; | ||
+ | int _tmain(int argc, _TCHAR* argv[]) | ||
+ | { | ||
+ | _tsetlocale(LC_ALL, _T("Japanese")); //ロケールセットすると_l関数でロケール指定しなくても日本語が使われます。 | ||
+ | |||
+ | _locale_t localeJpanease; | ||
+ | localeJpanease = _create_locale(LC_ALL, "Japanese"); | ||
+ | |||
+ | FILE* pfileText; | ||
+ | errno_t errNo; | ||
+ | int nCloseCnt; | ||
+ | wchar_t pwcStrFilePathShiftJIS[] = L"C:\\Users\\Administrator\\Documents\\マイ インフォメーション\\ファイル入出力\\test.txt"; | ||
+ | wchar_t pwcStrFilePathUtf8Bom[] = L"C:\\Users\\Administrator\\Documents\\マイ インフォメーション\\ファイル入出力\\test_utf8.txt"; | ||
+ | wchar_t pwcStrFilePathUtf8[] = L"C:\\Users\\Administrator\\Documents\\マイ インフォメーション\\ファイル入出力\\test_utf8N.txt"; | ||
+ | wchar_t pwcStrFilePathUtf16LE[] = L"C:\\Users\\Administrator\\Documents\\マイ インフォメーション\\ファイル入出力\\test_utf16LE.txt"; | ||
+ | |||
+ | wchar_t pwcStrGohan[] = L"ごはんを食べたよ。\r\n";//\r\n = CR LF = 0x0D 0x0A | ||
+ | char* pcStrStream; | ||
+ | wchar_t* pwcStrStream; | ||
+ | char* pcStrErr; | ||
+ | wchar_t* pwcStrErr; | ||
+ | |||
+ | pcStrStream = new char[1024]; | ||
+ | pwcStrStream = new wchar_t[1024]; | ||
+ | pcStrErr = new char[1024]; | ||
+ | pwcStrErr = new wchar_t[1024]; | ||
+ | |||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | //★UTF8のBOM無しテキストの読み込み | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | |||
+ | //※読み込みバッファを1024文字にしているので、1024文字以上の行を一続きにさせたい場合は、工夫が必要。それは後で考えます。 | ||
+ | //※行ごとにバッファの文字数を動的に確保したい場合、 | ||
+ | // 一文字づつスキャンして改行コードが現れるところまでのバイト長を確保するという手法もありそう。 | ||
+ | |||
+ | fpos_t* fposPos = new fpos_t; | ||
+ | |||
+ | printf("★UTF8のBOM無しテキストの読み込み\n"); | ||
+ | errNo = _wfopen_s( &pfileText, pwcStrFilePathUtf8, L"r,ccs=UTF-8"); | ||
+ | printf("Error?->%d\n", errNo); | ||
+ | |||
+ | fgetpos(pfileText, fposPos); | ||
+ | printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。BOM無しだと0文字目。 | ||
+ | |||
+ | //Unicodeサンプル文字 1行目 | ||
+ | printf("文字コードUTF-16LE->"); | ||
+ | for(int i=0; i < (int)wcslen(pwcStrGohan) + 1; i++){ | ||
+ | printf("%04x:",*(pwcStrGohan + i)); | ||
+ | } | ||
+ | printf("\n"); | ||
+ | |||
+ | printf("utf8N.txtテキストファイルの中身とその他の情報出力\n"); | ||
+ | while(!feof(pfileText)){ | ||
+ | |||
+ | pwcStrErr = fgetws(pwcStrStream,1024,pfileText);//ShiftJIS読み込み。最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。 | ||
+ | //先頭からUTF-8→UTF16LE形式で文字列を取得する。 | ||
+ | wprintf(L"%s(%s)\n", pwcStrStream, pwcStrErr);//UTF-16LEのpwcStrStreamをSJIS(Locale設定)で書き出し。UTF-16LEのままだと文字化けする。 | ||
+ | |||
+ | printf("文字コードUTF-16LE->"); | ||
+ | for(int i=0 ;i < 20 ;i++){ //Unicodeに変換された文字コードを先頭20文字だけ出力。 | ||
+ | printf("%04x:",*(pwcStrStream + i)); | ||
+ | } | ||
+ | |||
+ | fgetpos(pfileText, fposPos); | ||
+ | printf("fpos-> %04d\n", *fposPos); | ||
+ | } | ||
+ | printf("[EOF]"); | ||
+ | printf("\n"); | ||
+ | |||
+ | nCloseCnt = fclose(pfileText); | ||
+ | printf("nCloseErr->%d\n", nCloseCnt); | ||
+ | |||
+ | nCloseCnt = _fcloseall(); | ||
+ | printf("nCloseCnt->%d\n", nCloseCnt); | ||
+ | printf("\n"); | ||
+ | |||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | //★UTF8NのBOM有りテキストの読み込み | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | |||
+ | printf("★UTF8NのBOM有りテキストの読み込み\n"); | ||
+ | errNo = _wfopen_s( &pfileText, pwcStrFilePathUtf8Bom, L"r,ccs=UTF-8"); | ||
+ | printf("Error?->%d\n", errNo); | ||
+ | |||
+ | fgetpos(pfileText, fposPos); | ||
+ | printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。BOM無しだと3(0からカウントなので実際は4byte目)。 | ||
+ | |||
+ | printf("utf8.txtテキストファイルの中身\n"); | ||
+ | while(!feof(pfileText)){ | ||
+ | |||
+ | pwcStrErr = fgetws(pwcStrStream,1024,pfileText);//ShiftJIS読み込み。最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。 | ||
+ | //UTF-8(BOMはEF BB BFなので4バイト目)からUTF16LE形式で文字列を取得する。 | ||
+ | //※FE FFをUTF-8でエンコードするとEF BB BF | ||
+ | //※FE FF = 1111 1110 1111 1111 を | ||
+ | // 1111 11 10 11 11 1111 を | ||
+ | // 1110 (XXXX) 10(XX) (XX)(YY) 10(YY) (YYYY)と割り当て | ||
+ | //1110 1111 1011 1011 1011 1111 = EF BB BF | ||
+ | |||
+ | if(pwcStrErr != NULL ){ | ||
+ | wprintf(L"%s", pwcStrStream);//UTF-16LEのpwcStrStreamをSJIS(Locale設定)で書き出し。UTF-16LEのままだと文字化けする。 | ||
+ | } | ||
+ | } | ||
+ | printf("[EOF]"); | ||
+ | printf("\n"); | ||
+ | |||
+ | nCloseCnt = fclose(pfileText); | ||
+ | printf("nCloseErr->%d\n", nCloseCnt); | ||
+ | printf("\n"); | ||
+ | |||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | //★UTF16のBOM有りテキストの読み込み | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | |||
+ | printf("★UTF16LEのBOM有りテキストの読み込み\n"); | ||
+ | errNo = _wfopen_s( &pfileText, pwcStrFilePathUtf16LE, L"r,ccs=UTF-16LE"); | ||
+ | printf("Error?->%d\n", errNo); | ||
+ | |||
+ | fgetpos(pfileText, fposPos); | ||
+ | printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。BOM有りで2(0からカウントなので実際は3byte目)。 | ||
+ | |||
+ | printf("utf8.txtテキストファイルの中身\n"); | ||
+ | while(!feof(pfileText)){ | ||
+ | |||
+ | pwcStrErr = fgetws(pwcStrStream,1024,pfileText);//ShiftJIS読み込み。最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。 | ||
+ | if(pwcStrErr != NULL ){ //UTF-16(BOMはFE FFなので3バイト目)からUTF16LE形式で文字列を取得する。 | ||
+ | wprintf(L"%s", pwcStrStream);//UTF-16LEのpwcStrStreamをSJIS(Locale設定)で書き出し。UTF-16LEのままだと文字化けする。 | ||
+ | } | ||
+ | } | ||
+ | printf("[EOF]"); | ||
+ | printf("\n"); | ||
+ | |||
+ | nCloseCnt = fclose(pfileText); | ||
+ | printf("nCloseErr->%d\n", nCloseCnt); | ||
+ | printf("\n"); | ||
+ | |||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | //★ShiftJISテキストの読み込み | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | |||
+ | printf("★ShiftJISテキストの読み込み\n"); | ||
+ | errNo = _wfopen_s( &pfileText, pwcStrFilePathShiftJIS, L"r"); | ||
+ | printf("Error?->%d\n", errNo); | ||
+ | |||
+ | fgetpos(pfileText, fposPos); | ||
+ | printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。 | ||
+ | |||
+ | printf("test.txtテキストファイルの中身\n"); | ||
+ | while(!feof(pfileText)){ | ||
+ | |||
+ | pcStrErr = fgets(pcStrStream, 1024, pfileText);//ShiftJIS読み込み。最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。 | ||
+ | if(pcStrErr != NULL ){ | ||
+ | printf("%s", pcStrStream); | ||
+ | } | ||
+ | } | ||
+ | printf("[EOF]"); | ||
+ | printf("\n"); | ||
+ | |||
+ | nCloseCnt = fclose(pfileText); | ||
+ | printf("nCloseErr->%d\n", nCloseCnt); | ||
+ | printf("\n"); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | 出力結果 | ||
+ | <syntaxhighlight lang="text"> | ||
+ | ★UTF8のBOM無しテキストの読み込み | ||
+ | Error?->0 | ||
+ | fpos-> 0000 | ||
+ | 文字コードUTF-16LE->3054:306f:3093:3092:98df:3079:305f:3088:3002:000d:000a:0000: | ||
+ | utf8N.txtテキストファイルの中身とその他の情報出力 | ||
+ | ごはんを食べたよ。 | ||
+ | (ごはんを食べたよ。 | ||
+ | ) | ||
+ | 文字コードUTF-16LE->3054:306f:3093:3092:98df:3079:305f:3088:3002:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0029 | ||
+ | |||
+ | ( | ||
+ | ) | ||
+ | 文字コードUTF-16LE->000a:0000:3093:3092:98df:3079:305f:3088:3002:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0031 | ||
+ | |||
+ | ( | ||
+ | ) | ||
+ | 文字コードUTF-16LE->000a:0000:3093:3092:98df:3079:305f:3088:3002:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0033 | ||
+ | それだけだよ。 | ||
+ | (それだけだよ。 | ||
+ | ) | ||
+ | 文字コードUTF-16LE->305d:308c:3060:3051:3060:3088:3002:000a:0000:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0056 | ||
+ | それだけだよ。 | ||
+ | ((null)) | ||
+ | 文字コードUTF-16LE->305d:308c:3060:3051:3060:3088:3002:000a:0000:000a:0000:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:cdcd:fpos-> 0056 | ||
+ | [EOF] | ||
+ | nCloseErr->0 | ||
+ | nCloseCnt->0 | ||
+ | |||
+ | ★UTF8NのBOM有りテキストの読み込み | ||
+ | Error?->0 | ||
+ | fpos-> 0003 | ||
+ | utf8.txtテキストファイルの中身 | ||
+ | ごはんを食べたよ。 | ||
+ | |||
+ | |||
+ | それだけだよ。 | ||
+ | [EOF] | ||
+ | nCloseErr->0 | ||
+ | |||
+ | ★UTF16LEのBOM有りテキストの読み込み | ||
+ | Error?->0 | ||
+ | fpos-> 0002 | ||
+ | utf8.txtテキストファイルの中身 | ||
+ | ごはんを食べたよ。 | ||
+ | |||
+ | |||
+ | それだけだよ。 | ||
+ | [EOF] | ||
+ | nCloseErr->0 | ||
+ | |||
+ | ★ShiftJISテキストの読み込み | ||
+ | Error?->0 | ||
+ | fpos-> 0000 | ||
+ | test.txtテキストファイルの中身 | ||
+ | ごはんを食べたよ。 | ||
+ | |||
+ | |||
+ | それだけだよ。 | ||
+ | [EOF] | ||
+ | nCloseErr->0 | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | という感じです。ごはんを食べたよ それだけだよ。というサンプルの文字列に特に深い意味はありませんが、Windowsは改行コードにCR LFを使うのが一般的です。しかしながら、ファイルから取り込んできた文字列にでは\r 0x0dは無くなって\nにあたる0x0aだけが残る仕様になっています。これらの改行復帰コードを厳密に扱う場合には書き込みするときや、文字列操作時に工夫が必要になります。文字コードがどんなふうに扱われているのか、厳密にどうなっているのか調べるためにも、時々は文字コードそのものを出力して確かめるのも大事なのではないかと思います。人間様にわかる文字だけを出力していたのでは、コンピュータを正確にあやつることは難しいです。 | ||
=='''文字列と数値の変換'''== | =='''文字列と数値の変換'''== |