C 文字列操作 新しいページはコチラ
提供: yonewiki
(→文字列の検索と置換) |
(→文字列ファイルへの入出力) |
||
4,906行: | 4,906行: | ||
ここの長ったらしい文字列処理の説明の総括的なプログラミングになっているかと思います。全部理解してこそ、やりきれる処理です。文字列処理のプログラムを普段作らない自分でもこの程度なら理解しておきたいものです。ここのサンプルは直線的なプログラムで無駄の多いサンプルです。みなさんはもっともっと効率のいい文字列処理をやって下さい。ここでは関数の引数や、クラスの考え方をあまり知らなくても、文字列処理ができるように、あまり多くの技術を使わないそういう無駄なサンプルになっていますが、上から順番に処理していくだけなので、処理の順番は理解しやすいようになっているのではないかと自負するところです。あとは文字コードの自動判定くらいを触れて、文字列処理に関するこの記事は終了にしたいなぁと思います。誰も読まないかもしれないのに、よくこんなにウダウダと長ったらしい文章を書いたなぁ。恐るべしだ。でも、こういううだうだした説明ともっと若いときに出会っていたら、もっと手早く理解できたのになぁと自分は思います。この長い文書が誰かの役に立っていたなら幸せなことです。 | ここの長ったらしい文字列処理の説明の総括的なプログラミングになっているかと思います。全部理解してこそ、やりきれる処理です。文字列処理のプログラムを普段作らない自分でもこの程度なら理解しておきたいものです。ここのサンプルは直線的なプログラムで無駄の多いサンプルです。みなさんはもっともっと効率のいい文字列処理をやって下さい。ここでは関数の引数や、クラスの考え方をあまり知らなくても、文字列処理ができるように、あまり多くの技術を使わないそういう無駄なサンプルになっていますが、上から順番に処理していくだけなので、処理の順番は理解しやすいようになっているのではないかと自負するところです。あとは文字コードの自動判定くらいを触れて、文字列処理に関するこの記事は終了にしたいなぁと思います。誰も読まないかもしれないのに、よくこんなにウダウダと長ったらしい文章を書いたなぁ。恐るべしだ。でも、こういううだうだした説明ともっと若いときに出会っていたら、もっと手早く理解できたのになぁと自分は思います。この長い文書が誰かの役に立っていたなら幸せなことです。 | ||
+ | |||
+ | |||
+ | ICUによる文字コード自動判定のプログラムは以下のとおりです。 | ||
+ | <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> //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 _End | ||
+ | |||
+ | //ICU ucnvプリプロセッサ(ライブラリはデバッグモードとリリースモード個別設定) | ||
+ | #include <unicode/ucnv.h> //ucnv文字コード変換ライブラリヘッダ | ||
+ | #include <unicode/translit.h> //文字変換ライブラリヘッダ | ||
+ | #include <unicode/regex.h> //正規表現 | ||
+ | #include <unicode/ucsdet.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") //ICU Transliterate関数を使うために必要なライブラリ 正規表現関数も | ||
+ | #pragma comment(lib, "icuuc.lib") //ICU ucnvを使うために必要なライブラリ | ||
+ | //#pragma comment(lib, "icuio.lib") | ||
+ | #endif | ||
+ | //ICU ucnv _End | ||
+ | |||
+ | #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; | ||
+ | fpos_t* fposPos = new fpos_t; | ||
+ | errno_t errNo; | ||
+ | size_t* psizeRuturnValue = new size_t; | ||
+ | |||
+ | wchar_t pwcStrFilePath[] = L"C:\\Users\\Administrator\\Documents\\マイ インフォメーション\\ファイル入出力\\test_utf16LE.txt"; | ||
+ | char* pcStrStream; | ||
+ | pcStrStream = new char[1024]; | ||
+ | int nCloseResult = 0; | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | //★テキストの読み込み | ||
+ | //★★★★★★★★★★★★★★★★★★★★★★★★ | ||
+ | |||
+ | //int型でchar配列のサイズを動的に確保ができるので、int型の範囲のファイルなら | ||
+ | //一度に格納できるプログラムになっています。intの範囲を超えると動的メモリ確保で | ||
+ | //失敗します。その先の工夫はまた別途考える必要があります。少し筒変換したり…。 | ||
+ | |||
+ | int nSize = 0; | ||
+ | int nFeedCnt = 0; | ||
+ | |||
+ | char* pcStrFile; | ||
+ | fpos_t* pfposStartPos = new fpos_t; | ||
+ | printf("★テキストの読み込み\n"); | ||
+ | errNo = _wfopen_s( &pfileText, pwcStrFilePath, L"rb"); | ||
+ | printf("Error?->%d\n", errNo); | ||
+ | |||
+ | fgetpos(pfileText, fposPos); | ||
+ | printf("fpos-> %04d\n", *fposPos); //ファイルを開いた時点でのポジションの確認。 | ||
+ | |||
+ | printf("test.txtテキストファイルの中身\n"); | ||
+ | fgetpos(pfileText, pfposStartPos); | ||
+ | while(!feof(pfileText)){ | ||
+ | nSize = nSize + nFeedCnt * 1024; | ||
+ | nFeedCnt++; | ||
+ | *psizeRuturnValue = fread(pcStrStream, sizeof(char), 1024, pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。 | ||
+ | fgetpos(pfileText, fposPos); | ||
+ | for(int i = 0; i < *fposPos; i++){ | ||
+ | printf("%02x:",0x000000FF & *(pcStrStream + i)); | ||
+ | } | ||
+ | } | ||
+ | printf("[EOF]"); | ||
+ | printf("\n"); | ||
+ | |||
+ | nSize = nSize + (int)*fposPos - ((nFeedCnt - 1) * 1024); | ||
+ | fsetpos(pfileText, pfposStartPos); | ||
+ | pcStrFile = new char[nSize + 1]; | ||
+ | while(!feof(pfileText)){ | ||
+ | *psizeRuturnValue = fread(pcStrFile, sizeof(char), nSize + 1, pfileText);//最終行がEOFのみの場合に失敗して1行前の値が残ることに注意。 | ||
+ | for(int i = 0; i < nSize; i++){ | ||
+ | printf("%02x:",0x000000FF & *(pcStrFile + i)); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf("[EOF]"); | ||
+ | printf("\n"); | ||
+ | fgetpos(pfileText, fposPos); | ||
+ | printf("fpos-> %04d\npReturnValue-> %04d\n", *fposPos, *psizeRuturnValue); //ファイルを開いた時点でのポジションの確認。 | ||
+ | |||
+ | |||
+ | nCloseResult = fclose(pfileText); | ||
+ | printf("nCloseErr->%d\n", nCloseResult); | ||
+ | printf("\n"); | ||
+ | |||
+ | UConverter* ucnvTest; | ||
+ | UErrorCode uerrorNum = U_ZERO_ERROR; | ||
+ | int nStrSize; | ||
+ | int nStrResultSize; | ||
+ | char pcStrUTF16LEBOM[] = "\xFF\xFE"; | ||
+ | char pcStrUTF8BOM[] = "\xEF\xBB\xBF"; | ||
+ | nStrSize = nSize; | ||
+ | |||
+ | UCharsetDetector* pucsetdetecDetector = ucsdet_open(&uerrorNum); | ||
+ | ucsdet_setText(pucsetdetecDetector, pcStrFile, nSize, &uerrorNum); | ||
+ | |||
+ | int32_t int32FindCharSetCnt = 0; | ||
+ | const UCharsetMatch* pucsetmatchStrCode = ucsdet_detect(pucsetdetecDetector, &uerrorNum); | ||
+ | |||
+ | const char* cpcDetectStrCodeName; | ||
+ | const char* cpcDetectStrCodeLanguage; | ||
+ | cpcDetectStrCodeName = ucsdet_getName(pucsetmatchStrCode, &uerrorNum);//文字コード名 ICUの文字コードセット名に適した形式。 | ||
+ | cpcDetectStrCodeLanguage = ucsdet_getLanguage(pucsetmatchStrCode, &uerrorNum); | ||
+ | |||
+ | printf("★ICU Code Detectorによるテキストの文字コード自動判定\n"); | ||
+ | printf("CodeName->%s\n",cpcDetectStrCodeName); | ||
+ | printf("CodeLang->%s\n",cpcDetectStrCodeLanguage);//RFC3066コードを取得できる。 | ||
+ | |||
+ | //Shift_JISからUTF-16LEへの変換処理 | ||
+ | |||
+ | //先頭がBOMコードなら変換する文字の先頭アドレスをBOMコードの長さを割り出し、nShiftByteに記録。 | ||
+ | int nShiftByte = 0; | ||
+ | if(strlen(pcStrFile) >= 3){ | ||
+ | if(strncmp(pcStrFile,pcStrUTF8BOM,3) == 0){ | ||
+ | nShiftByte = 3; | ||
+ | } | ||
+ | } | ||
+ | if(strlen(pcStrFile) >= 2){ | ||
+ | if(strncmp(pcStrFile,pcStrUTF16LEBOM,2) == 0){ | ||
+ | nShiftByte = 2; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | std::wstring stringCnvResult(nStrSize, L'\0'); | ||
+ | |||
+ | ucnvTest = ucnv_open(cpcDetectStrCodeName, &uerrorNum);//変換する文字コードは自動判定で取得した文字コード。 | ||
+ | nStrResultSize = ucnv_toUChars( | ||
+ | ucnvTest, | ||
+ | &stringCnvResult[0], stringCnvResult.size(), // 変換先のポインタとサイズ | ||
+ | &pcStrFile[0 + nShiftByte], nStrSize - nShiftByte, // 変換元のポインタとサイズ BOMサイズがnShiftByteに格納されている。 | ||
+ | &uerrorNum | ||
+ | ); | ||
+ | stringCnvResult.resize(nStrResultSize); | ||
+ | ucnv_close(ucnvTest); | ||
+ | ucsdet_close(pucsetdetecDetector); | ||
+ | _tprintf(_T("%s[EOF]"), stringCnvResult.c_str()); | ||
+ | printf("\n"); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | 出力結果 utf16LEのテキストを指定した場合。 | ||
+ | <syntaxhighlight lang="text"> | ||
+ | ★テキストの読み込み | ||
+ | Error?->0 | ||
+ | fpos-> 0000 | ||
+ | test.txtテキストファイルの中身 | ||
+ | ff:fe:54:30:6f:30:93:30:92:30:df:98:79:30:5f:30:88:30:02:30:0d:00:0a:00:0d:00:0a:00:0d:00:0a:00:5d:30:8c:30:60:30:51:30:60:30:88:30:02:30:0d:00:0a:00:[EOF] | ||
+ | ff:fe:54:30:6f:30:93:30:92:30:df:98:79:30:5f:30:88:30:02:30:0d:00:0a:00:0d:00:0a:00:0d:00:0a:00:5d:30:8c:30:60:30:51:30:60:30:88:30:02:30:0d:00:0a:00:[EOF] | ||
+ | fpos-> 0050 | ||
+ | pReturnValue-> 0000 | ||
+ | nCloseErr->0 | ||
+ | |||
+ | ★ICU Code Detectorによるテキストの文字コード自動判定 | ||
+ | CodeName->UTF-16LE | ||
+ | CodeLang->UTF-16LE | ||
+ | ごはんを食べたよ。 | ||
+ | |||
+ | |||
+ | それだけだよ。 | ||
+ | [EOF] | ||
+ | </syntaxhighlight> | ||
+ | 出力結果 utf8のテキストを指定した場合。 | ||
+ | <syntaxhighlight lang="text"> | ||
+ | ★テキストの読み込み | ||
+ | Error?->0 | ||
+ | fpos-> 0000 | ||
+ | test.txtテキストファイルの中身 | ||
+ | ef:bb:bf:e3:81:94:e3:81:af:e3:82:93:e3:82:92:e9:a3:9f:e3:81:b9:e3:81:9f:e3:82:88:e3:80:82:0d:0a:0d:0a:0d:0a:e3:81:9d:e3:82:8c:e3:81:a0:e3:81:91:e3:81:a0:e3:82:88:e3:80:82:0d:0a:[EOF] | ||
+ | ef:bb:bf:e3:81:94:e3:81:af:e3:82:93:e3:82:92:e9:a3:9f:e3:81:b9:e3:81:9f:e3:82:88:e3:80:82:0d:0a:0d:0a:0d:0a:e3:81:9d:e3:82:8c:e3:81:a0:e3:81:91:e3:81:a0:e3:82:88:e3:80:82:0d:0a:[EOF] | ||
+ | fpos-> 0059 | ||
+ | pReturnValue-> 0000 | ||
+ | nCloseErr->0 | ||
+ | |||
+ | ★ICU Code Detectorによるテキストの文字コード自動判定 | ||
+ | CodeName->UTF-8 | ||
+ | CodeLang->UTF-8 | ||
+ | ごはんを食べたよ。 | ||
+ | |||
+ | |||
+ | それだけだよ。 | ||
+ | [EOF] | ||
+ | </syntaxhighlight> | ||
+ | 出力結果 JISのテキストを指定した場合。 | ||
+ | <syntaxhighlight lang="text"> | ||
+ | ★テキストの読み込み | ||
+ | Error?->0 | ||
+ | fpos-> 0000 | ||
+ | test.txtテキストファイルの中身 | ||
+ | 1b:24:42:24:34:24:4f:24:73:24:72:3f:29:24:59:24:3f:24:68:21:23:1b:28:4a:0d:0a:0d:0a:0d:0a:1b:24:42:24:3d:24:6c:24:40:24:31:24:40:24:68:21:23:1b:28:4a:0d:0a:[EOF] | ||
+ | 1b:24:42:24:34:24:4f:24:73:24:72:3f:29:24:59:24:3f:24:68:21:23:1b:28:4a:0d:0a:0d:0a:0d:0a:1b:24:42:24:3d:24:6c:24:40:24:31:24:40:24:68:21:23:1b:28:4a:0d:0a:[EOF] | ||
+ | fpos-> 0052 | ||
+ | pReturnValue-> 0000 | ||
+ | nCloseErr->0 | ||
+ | |||
+ | ★ICU Code Detectorによるテキストの文字コード自動判定 | ||
+ | CodeName->ISO-2022-JP | ||
+ | CodeLang->ISO-2022-JP | ||
+ | ごはんを食べたよ。 | ||
+ | |||
+ | |||
+ | それだけだよ。 | ||
+ | [EOF] | ||
+ | </syntaxhighlight> | ||
+ | 出力結果 ShiftJISのテキストを指定した場合。 | ||
+ | <syntaxhighlight lang="text"> | ||
+ | ★テキストの読み込み | ||
+ | Error?->0 | ||
+ | fpos-> 0000 | ||
+ | test.txtテキストファイルの中身 | ||
+ | 82:b2:82:cd:82:f1:82:f0:90:48:82:d7:82:bd:82:e6:81:42:0d:0a:0d:0a:0d:0a:82:bb:82:ea:82:be:82:af:82:be:82:e6:81:42:0d:0a:[EOF] | ||
+ | 82:b2:82:cd:82:f1:82:f0:90:48:82:d7:82:bd:82:e6:81:42:0d:0a:0d:0a:0d:0a:82:bb:82:ea:82:be:82:af:82:be:82:e6:81:42:0d:0a:[EOF] | ||
+ | fpos-> 0040 | ||
+ | pReturnValue-> 0000 | ||
+ | nCloseErr->0 | ||
+ | |||
+ | ★ICU Code Detectorによるテキストの文字コード自動判定 | ||
+ | CodeName->Shift_JIS | ||
+ | CodeLang->Shift_JIS | ||
+ | ごはんを食べたよ。 | ||
+ | |||
+ | |||
+ | それだけだよ。 | ||
+ | [EOF] | ||
+ | </syntaxhighlight> | ||
+ | とこのような感じになります。書き出すときには文字コード別にBOMを付与する処理が必要になりますが、自動判別によって、かなりたくさんの文字コードのテキストファイルを自動で読み込むことができるようになります。やや便利なコンソールアプリになったのかもしれません。そんな得体の知れないテキストファイルが転がっているかどうかは知りませんが…。BOMコードの種類にはUTF-16BEもあるので、このあたりにも対応したプログラムにするともっといいかもしれませんね。 | ||
+ | 今回のサンプルでは完璧な変換をしてくれましたが、ややこしい文字構成ばかりのテキストファイルになってくると間違えることもあります。そのためICUでは文字コード一致率みたいなランキングを作ってくれる関数もありまして、ucsdet_detect関数の代わりにucsdet_detectAllという関数で UCharsetMatch**型を戻り値にするものがあります。ポインタのポインタってことは、配列ってことになります。ucsdete_getName関数で、引数のその配列の要素を渡せば、それぞれの文字コード名が一覧されます。具体的には以下のような要領になります。 | ||
+ | <syntaxhighlight lang="cpp" line start="1"> | ||
+ | int32_t int32FindCharSetCnt = 0; | ||
+ | int32_t int32Confidence = 0; | ||
+ | const UCharsetMatch* pucsetmatchStrCode = ucsdet_detect(pucsetdetecDetector, &int32FindCharSetCnt &uerrorNum); | ||
+ | const char* cpcDetectStrCodeName; | ||
+ | const char* cpcDetectStrCodeLanguage; | ||
+ | |||
+ | cpcDetectStrCodeName = ucsdet_getName(pucsetmatchStrCode[配列番号], &uerrorNum);//文字コード名 ICUの文字コードセット名に適した形式。 | ||
+ | cpcDetectStrCodeLanguage = ucsdet_getLanguage(pucsetmatchStrCode[配列番号], &uerrorNum); | ||
+ | int32Confidence = ucsdet_getConfidence(pucsetmatchStrCode[配列番号], &uerrorNum); | ||
+ | </syntaxhighlight> | ||
+ | のようにするとConfidenceの値が小さいほど一致率が高いものとして確認ができます。全部を一覧するにはfor文で0~int32FindCharSetCnt -1までの要素を確認するとよいです。 | ||
=='''文字列と数値の変換'''== | =='''文字列と数値の変換'''== |