全角の文字列を指定文字数で2つの文字列に分割する。
全角の文字列を指定文字数で2つの文字列に分割する。
例えば、message"1234567891011121314"のような文字列があったとして(全角半角混合)、
これを指定文字数(例えば7)で分割して、mess1の"1234567"とmess2の"891011121314"に分割したいです。
また、指定文字数が30(実際の文字数より大きい)の時は、mess1に"1234567891011121314"、mess2に""(空っぽ)が入るような関数がほしいのです。
_tcsncpy_sを使う方法は前半は抜き出してmess1に入れても、mess2の方法は分からず、
mess2を右からの文字数で抜き出そうとしても、文字列が短すぎると、mess1とmess2に重なりができてしまいます。
一体どうすればいいのでしょうか。
代入するmessageはchar型なのですが、全角半角混合でcharの関数のままいじると1バイトだけ読み込むせいで端にゴミがうまれてしまうので、
おそらくtcharかwcharを使うべきなのはわかりますが…。
これを指定文字数(例えば7)で分割して、mess1の"1234567"とmess2の"891011121314"に分割したいです。
また、指定文字数が30(実際の文字数より大きい)の時は、mess1に"1234567891011121314"、mess2に""(空っぽ)が入るような関数がほしいのです。
_tcsncpy_sを使う方法は前半は抜き出してmess1に入れても、mess2の方法は分からず、
mess2を右からの文字数で抜き出そうとしても、文字列が短すぎると、mess1とmess2に重なりができてしまいます。
一体どうすればいいのでしょうか。
代入するmessageはchar型なのですが、全角半角混合でcharの関数のままいじると1バイトだけ読み込むせいで端にゴミがうまれてしまうので、
おそらくtcharかwcharを使うべきなのはわかりますが…。
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
素直に必要な文字数だけコピーすればいいと思います。
#include <stdio.h>
#include <wchar.h>
void bunkatu(const wchar_t* in,wchar_t* mess1,wchar_t* mess2,int mozisu) {
int i;
for(i=0;i<mozisu;i++) {
*mess1=*in;
if(*in==L'\0')break;
mess1++;in++;
}
*mess1=L'\0';
while((*(mess2++)=*(in++))!=L'\0');
}
int main(void) {
wchar_t buffer[100]=L"1234567891011121314";
wchar_t mess1[100],mess2[100];
bunkatu(buffer,mess1,mess2,7);
fputws(mess1,stdout);putwc(L'\n',stdout);
fputws(mess2,stdout);putwc(L'\n',stdout);
bunkatu(buffer,mess1,mess2,30);
fputws(mess1,stdout);putwc(L'\n',stdout);
fputws(mess2,stdout);putwc(L'\n',stdout);
return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
文字コードで全角文字判定して処理することもできます。
このコードの場合\0で文字列終了部分を変えているだけなので文字列\0以降にデータが残っていることに注意してください。
このコードの場合\0で文字列終了部分を変えているだけなので文字列\0以降にデータが残っていることに注意してください。
#include <stdio.h>
#include <string.h>
void separate(int charNum, const char *input, char *mess1, char *mess2);
int main(void)
{
char message[100] = "01234567891011121314";
char mess1[100];
char mess2[100];
separate(7, message, mess1, mess2);
printf("mess1=%s, mess2=%s\n", mess1, mess2);
separate(20, message, mess1, mess2);
printf("mess1=%s, mess2=%s\n", mess1, mess2);
separate(30, message, mess1, mess2);
printf("mess1=%s, mess2=%s\n", mess1, mess2);
getchar();
return 0;
}
// 全角文字の間を分割したら文字化けするのでしないように全角文字も1文字と見なします
void separate(int charNum, const char *input, char *mess1, char *mess2)
{
int charCount = 0;
int byteNum = 0;
unsigned char *check_doublebyte = (unsigned char *)input;
while (charCount < charNum){
// 全角第1バイトかどうか判定(SHIFT-JIS)
if ((check_doublebyte[byteNum] >= 0x81 && check_doublebyte[byteNum] <= 0x9e) ||
(check_doublebyte[byteNum] >= 0xe0 && check_doublebyte[byteNum] <= 0xef)){
if (input[byteNum + 1] == '\0'){
byteNum++;
break;
}
else{
byteNum += 2;
}
}
else{
byteNum++;
}
if (input[byteNum] == '\0'){
byteNum--;
}
charCount++;
}
strcpy(mess1, input);
strcpy(mess2, input);
mess1[byteNum + 1] = '\0';
mess2[0] = mess2[byteNum + 1];
}
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
pg78445さん
そのコードでは、うまく動かないはずです。
https://ideone.com/Xk4WAe (入力を半角文字のみにしてテスト)
58行目、61行目が怪しいです。
ちゃんと自分でテストしましたか?
pg78445さんの手元の環境ではこれで正常に動作するのであれば、
その処理系(OS、コンパイラ)を教えてください。
そのコードでは、うまく動かないはずです。
https://ideone.com/Xk4WAe (入力を半角文字のみにしてテスト)
58行目、61行目が怪しいです。
ちゃんと自分でテストしましたか?
pg78445さんの手元の環境ではこれで正常に動作するのであれば、
その処理系(OS、コンパイラ)を教えてください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
素直に先頭から1文字ずつ調べては結果領域に代入してみました.
inline
bool IsSJIS_LeadByte( unsigned char c )
{ return ( 0x81<=c && c<=0x9F ) || ( 0xE0<=c && c<=0xFC ); }
//Srcの先頭n1文字をpDst1に,残りをpDst2に入れる.
//全角文字も1文字とカウントする.
//※pDst1,pDst2は十分なサイズの領域を指している前提.
void SJIS_str_split( const char *Src, size_t n1, char *pDst1, char *pDst2 )
{
const char *iSrc = Src;
pDst1[0] = pDst2[0] = '\0';
char *pDst = ( n1>0 ? pDst1 : pDst2 );
size_t counter = 0;
while( *iSrc != '\0' )
{
if( IsSJIS_LeadByte( (unsigned char)(*iSrc) ) )
{
*pDst++ = *iSrc++;
*pDst++ = *iSrc++;
}
else
{ *pDst++ = *iSrc++; }
if( ++counter == n1 )
{
*pDst = '\0';
pDst = pDst2;
}
}
*pDst = '\0';
}
//
void SplitAndShow( const char *Message, size_t n1, char *mess1, char *mess2 )
{
std::cout << "n1 = " << n1 << " :" << std::endl;
SJIS_str_split( Message, n1, mess1, mess2 );
std::cout
<< "mess1= \"" << mess1 << "\"" << std::endl
<< "mess2= \"" << mess2 << "\"" << std::endl
<< std::endl;
}
//
int main( int argc, char **argv )
{
const int N = 20;
char Message[N] = "Cat「にゃーん!!」";
char mess1[N] = { '\0' };
char mess2[N] = { '\0' };
std::cout << "Message= \"" << Message << "\"" << std::endl << std::endl;
SplitAndShow( Message, 0, mess1, mess2 );
SplitAndShow( Message, 5, mess1, mess2 );
SplitAndShow( Message, 7, mess1, mess2 );
SplitAndShow( Message, 11, mess1, mess2 );
SplitAndShow( Message, 12, mess1, mess2 );
//
std::cout << "[end]" << std::endl;
std::cin.ignore();
return 0;
}
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
Shift_JIS用のサンプルは既に出ているので、UTF-8用のサンプルを置いておきます。
https://ideone.com/9kxjXZ
#include <stdio.h>
/* UTF8の次の文字をoutに格納し、そのバイト数を返す。
* 不正バイト列のチェックは行わない。
* (bufの先頭がナル文字でなければ)outにナル文字は格納しない。
*/
int get_next_utf8_char(char *out, const char *buf) {
int bytes = 1;
int i;
if ((buf[0] & 0x80) == 0x00) {
bytes = 1;
} else {
int now_mask = 0xE0;
int now_puttern = 0xC0;
for (i = 2; i <= 6; i++) {
if ((buf[0] & now_mask) == now_puttern) {
bytes = i;
break;
}
now_mask = (now_mask >> 1) | 0x80;
now_puttern = (now_puttern >> 1) | 0x80;
}
}
for (i = 0; i < bytes; i++) out[i] = buf[i];
return bytes;
}
void split_string(const char *input, char *mess1, char *mess2, int length) {
int i;
for (i = 0; *input != '\0' && i < length; i++) {
int length = get_next_utf8_char(mess1, input);
mess1 += length;
input += length;
}
*mess1 = '\0';
while (*input != '\0') {
int length = get_next_utf8_char(mess2, input);
mess2 += length;
input += length;
}
*mess2 = '\0';
}
int main(void) {
const int sample_length[] = {0, 5, 7, 11, 12, 30, -1};
const char* samples[] = {
"1234567891011121314",
"Cat「にゃーん!!」",
/* 1件のテストケースを省略 : Ideone.com参照 */
NULL
};
char mess1[100], mess2[100];
int i, j;
for (i = 0; sample_length[i] >= 0; i++) {
printf("length = %d\n", sample_length[i]);
for (j = 0; samples[j] != NULL; j++) {
split_string(samples[j], mess1, mess2, sample_length[i]);
printf("(input, mess1, mess2) = (\"%s\", \"%s\", \"%s\")\n",
samples[j], mess1, mess2);
}
}
return 0;
}
オフトピック
3番目のテストケースを入れた状態で投稿しようとすると
と言われてしまいました…。SQL ERROR [ mysqli ]
Incorrect string value: '\xF0\xA6\xA5\x91\xF0\xA6...' for column 'post_text' at row 1 [1366]
SQLエラー が発生したため、ページ情報をデータベースから取得できませんでした。問題が解決しない場合は管理人にご連絡ください。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
環境は
Windows 7
microsoft visual studio express 2013 for windows desktop
です。SHIFT-JIS前提で決め打ちしてます。
http://charset.7jp.net/sjis.html
ideoneはutf-8みたいですね。
https://ideone.com/Eh8arn
http://ash.jp/code/unitbl21.htm
Windows 7
microsoft visual studio express 2013 for windows desktop
です。SHIFT-JIS前提で決め打ちしてます。
http://charset.7jp.net/sjis.html
ideoneはutf-8みたいですね。
https://ideone.com/Eh8arn
http://ash.jp/code/unitbl21.htm
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
皆さん有難うございます。
文字コードを指定していないせいで色んな可能性を作ってしまった感がありますね。すみませんでした・・・。
「環境」
Windows 7
Shift-JIS
(これはusaoさんのコードでバグが出なかったことから恐らく自分はShift-JISで書いているんだろうなという予測だけで、正式に確認したわけではないです。
「マルチバイト文字セットを使用する」というプロジェクトの設定は関係あるのでしょうか?)
.cppですが主にC言語で表記。
VC++ 2013です。(FrameworkのバージョンがXPに対応していないので、いずれVC++のバージョンを下げる可能性があります)
char→wchar_tの変換が文字化けで上手くいかず、Shift-JISでしか動かず、
結果、usaoさんのコードが全く思った通りに動くので、使用させていただきました。
文法がほとんど分からないのですが、よくみたら関数部分はC言語ですね。
普段知ってること文法だけで好き勝手書いてるので、知識が狭くて恥ずかしいです。
IsSJIS_LeadByteが恐らくそのcharが一文字か中途半端な部分じゃないかを判定して、
インプットしたメッセージの最初のアドレスをiSrcに入れて、順々にpDst1とpDst2を入れていってるんですよね。
後から仕様変更するような真似をして申し訳ないんですが、
実はデザインの都合上、3つに分ける必要があることに気付いてしまいました。
それぞれに分割する文字数を設定して、3つに分ける関数です。
夜遅いので今日はこのメッセージを投稿して、そのまま寝てしまいますが、
勉強のため丸投げせず自分でも読み解いてアレンジしてみようと思います。
文字コードを指定していないせいで色んな可能性を作ってしまった感がありますね。すみませんでした・・・。
「環境」
Windows 7
Shift-JIS
(これはusaoさんのコードでバグが出なかったことから恐らく自分はShift-JISで書いているんだろうなという予測だけで、正式に確認したわけではないです。
「マルチバイト文字セットを使用する」というプロジェクトの設定は関係あるのでしょうか?)
.cppですが主にC言語で表記。
VC++ 2013です。(FrameworkのバージョンがXPに対応していないので、いずれVC++のバージョンを下げる可能性があります)
char→wchar_tの変換が文字化けで上手くいかず、Shift-JISでしか動かず、
結果、usaoさんのコードが全く思った通りに動くので、使用させていただきました。
文法がほとんど分からないのですが、よくみたら関数部分はC言語ですね。
普段知ってること文法だけで好き勝手書いてるので、知識が狭くて恥ずかしいです。
IsSJIS_LeadByteが恐らくそのcharが一文字か中途半端な部分じゃないかを判定して、
インプットしたメッセージの最初のアドレスをiSrcに入れて、順々にpDst1とpDst2を入れていってるんですよね。
後から仕様変更するような真似をして申し訳ないんですが、
実はデザインの都合上、3つに分ける必要があることに気付いてしまいました。
それぞれに分割する文字数を設定して、3つに分ける関数です。
//恐らくこういう形になると思われる。
SJIS_str_split( const char *Src, size_t n1,size_t n2, char *pDst1, char *pDst2,char *pDst3 )
SplitAndShow( "12345678912345", 5, 7,mess1, mess2, mess3);
↓
mess1:"12345"
mess2:"6789123"
mess3:"45"
勉強のため丸投げせず自分でも読み解いてアレンジしてみようと思います。
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
setlocale(LC_CTYPE, "") を実行していないからありませんか?namari さんが書きました: char→wchar_tの変換が文字化けで上手くいかず、
指定した文字数に相当するバイト数を返す関数を用意すればよいのでは?namari さんが書きました: 実はデザインの都合上、3つに分ける必要があることに気付いてしまいました。
それぞれに分割する文字数を設定して、3つに分ける関数です。
#include <stdio.h>
#include <string.h>
int isLeading(unsigned char c) { return (c ^ 0x20) - 0xa1u < 60; }
size_t blen(const char *s, size_t n)
{
int i, k;
for (i = k = 0; k < n && s[i]; i++, k++)
if (isLeading(s[i])) i++;
return i;
}
void split3(const char *s, int n1, int n2, char *s1, char *s2, char *s3)
{
int k = blen(s, n1); memcpy(s1, s, k), s1[k] = 0;
k = blen(s += k, n2), memcpy(s2, s, k), s2[k] = 0;
strcpy(s3, s + k);
}
int main(void)
{
char mess1[100], mess2[100], mess3[100];
split3("12345678912345", 5, 7, mess1, mess2, mess3);
puts(mess1);
puts(mess2);
puts(mess3);
return 0;
}
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
先ほどは Shift-JIS 専用でしたが、今度は UTF-8版です。
Shift-JIS でも、UTF-8 でも、EUC でも何でもいいのが次のプログラムです。
#include <stdio.h>
#include <string.h>
size_t blen(const char *s, size_t n)
{
int i, j, k, c;
for (i = k = 0; k < n && (c = s[i]); k++, i++)
if (c & 0xc0)
for (j = 5; i++, c>>j & 1; j--) ;
return i;
}
void split3(const char *s, int n1, int n2, char *s1, char *s2, char *s3)
{
int k = blen(s, n1); memcpy(s1, s, k), s1[k] = 0;
k = blen(s += k, n2), memcpy(s2, s, k), s2[k] = 0;
strcpy(s3, s + k);
}
int main(void)
{
char mess1[100], mess2[100], mess3[100];
split3("12345678912345", 5, 7, mess1, mess2, mess3);
puts(mess1);
puts(mess2);
puts(mess3);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
size_t blen(const char *s, size_t n)
{
int i, j, k, c;
for (i = k = 0; k < n && s[i]; k++)
i += mblen(s + i, MB_CUR_MAX);
return i;
}
void split3(const char *s, int n1, int n2, char *s1, char *s2, char *s3)
{
int k = blen(s, n1); memcpy(s1, s, k), s1[k] = 0;
k = blen(s += k, n2), memcpy(s2, s, k), s2[k] = 0;
strcpy(s3, s + k);
}
int main(void)
{
char mess1[100], mess2[100], mess3[100];
setlocale(LC_CTYPE, "");
split3("12345678912345", 5, 7, mess1, mess2, mess3);
puts(mess1);
puts(mess2);
puts(mess3);
return 0;
}
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
まず、setlocaleの挿入でうまくいきました。
mbstowcsの説明を検索すると大概ちゃんと置かれているのに完全にスルーしてしまいした。
3つに分けるコードはうまくいきました!
非常に明快に書いてくださったおかげで、すっと理解出来ました。
とは言いますが、普段テンプレのようなfor文しか使っていなかったせいで、
for文の意味を掴むのに少し時間がかかってしまいましたが。お恥ずかしい。
昨日の段階では余りわかってなかったんですが、isLeadingやget_next_utf8_charやIsSJIS_LeadByteは
その文字コードにおいて、入れたcharが確かに全角文字の1バイト目か見て、そうなら
1つ次までポインタを進めて、区切りが変な位置に来ないようにしていたのですね。
別にテキストに重きを置くゲームは作っていないですが、今なら改行回数を任意の数にもできそうです!
試しにsplit4も作ってみましたが正常に動きました!(たった1行加えただけですが・・・。この明快さには感動です。)
本当にありがとうございました。
Re: 全角の文字列を指定文字数で2つの文字列に分割する。
すみません。トップページで解決表示になってませんね。かずまさんの返信での解決は解決扱いにならないのでしょうか。
解決です。
解決です。