JavaScriptで全角2文字、半角1文字でカウントする

前の「JavaScriptで文字数でなくてバイト数を求める」だとUTF8ベースなので、1文字が3バイトや4バイトになってしまう…

なので、今回は全角2文字、半角1文字でカウントする関数を作ってみました。
他の国の言葉は知らないので、完全ではないでしょうが、日本語だったらこれで大丈夫かなぁ…

不味かったらご指摘ください。

function bytes(str) {
	str = str.replace(/[。-゚]/g, 'K');
	var hex = '';
	for (var i = 0; i < str.length; i++) {
		hex += str.charCodeAt(i).toString(16);
	}
	return hex.length/2;
}

軽く解説:
まず引数の半角カタカナを1バイトでカウントするために、'K'に置き換えています。
次は、charCodeAt&toString(16)で文字コードを16進数に変換してます。
最後に、16進数に変換した文字列の文字数を2で割ってるだけです。
ちなみに、charCodeAtはUTF16の文字コードを10進数で返してくれます。
んでもって、toString(16)はNumberを16進数にしてくれます。
toString(16)の0埋めは、入力できる文字は0x20からだから気にしなくて良さそうだ。

追記:2020/04/21
theさまからご指摘いただいたので、修正しました。

function bytes(str) {
	str = str.replace(/[。-゚]/g, 'K');
	var hex = '';
	for (var i = 0; i < str.length; i++) {
		hex += (('0000' + str.charCodeAt(i).toString(16)).slice(-4)).replace(/^00/, '');   // <- ここ修正
	}
	return hex.length/2;
}

軽く解説:
改行やタブ文字は、toString(16)だと0埋めしてくれないので、
一旦4桁0埋めして、先頭の00を削除するようにしてみました。
これでたぶん大丈夫、、、

JavaScriptでダウンロード。 iPhoneフルスクリーンではできない!?

先日作った「みんな大好き!くじ引きメーカー」。
このウェブアプリにくじの内容をダウンロードとアップロードができる機能を追加しようとしたら、iPhoneでフルスクリーンの時にダウンロードができないという事態に…。
今日はその解決方法を記録しとこうと思います。

●まずはダウンロードさせる関数

function downloadData(data) {
	var a = document.createElement('a');
	a.download = 'kuji.txt';
	a.href = 'data:application/octet-stream,'+encodeURIComponent(data);
	a.click();
}

軽く解説:
aタグを作って、download属性、href属性をセットして、強制的にクリックしてるだけです。
ちなみにdownload属性はHTML5から使えるみたいなので、IE9以前とかは使えないみたいですね。
あと、data:スキームも…。

●で、iPhoneフルスクリーンでの挙動
ボタンがピコんってなるだけで、無反応。
クリックイベントは来てるのに…。(.addEventListenerで確認)

●ちなみに、iPhone Safariでは
download属性は効いていないけれどダウンロードできました。
ファイル名がUnknown.txtになってしまう。(ここはあきらめた)

●で、最終どうしたか
downloadDataを呼び出すところで、iPhoneとそれ以外で分岐するようにして、
iPhoneの場合はdownload.htmlにジャンプさせて、download.html側でdownloadDataと同じ処理をするようにしましたとさ。

・分岐部分

var userAgent = window.navigator.userAgent.toLowerCase();
if (userAgent.indexOf('iphone') != -1)	{
	var a = document.createElement('a');
	a.href = './download.html?data='+encodeURIComponent(data);
	a.click();	
} else
{
	downloadData(data);
}

軽く解説:
window.navigator.userAgentでユーザーエージェントが取ってこれるので、toLowerCaseで全て小文字にしてます。
小文字のユーザーエージェントにiphoneという文字列が入ってたら、download.htmlにジャンプ。
入っていなければdownloadDataを呼ぶようにしています。

・download.html

<html>
<head>
<script>
	var data = (window.location.search.substring(1)).replace(/^.*data=/, '').replace(/&.*$/, '');
	var a = document.createElement('a');
	a.download = 'kuji.txt';
	a.href = 'data:application/octet-stream,'+data;
	a.click();
</script>
</head>
<body>
</body>
</html>

軽く解説:
window.location.searchは、URLの?で始まるクエリ部分が取ってこれます。
なので、正規表現で先頭からdata=までを削除して、&から最後まで削除します。
で、残ったのがダウンロードさせたいデータです。
あとはdownloadData関数と同じです。
ちなみに、dataにはURLエンコードされた文字列が入っているので、encodeURIComponentは通しません。

◆まとめ
ファイル名がUnknow.txtになったり、別途Safariが起動したり、気に入らないところはあるものの、ひとまずダウンロード&アップロードができるようになったので、良しとすることにしました。
もっと良い方法があれば、だれか教えて欲しいなぁ・・・。

JavaScriptでスマホアプリもどき

先日公開した「みんな大好き!くじ引きメーカー」を作ってみて思ったこと。

とにかくスマホアプリっぽいのを少ない工数で作りたかったので、JavaScript+CSS+HTML5だけで実装した。
この方法の良かった点と良くなかった点をまとめてみました。

●良かった点
 ・JavaScriptなので、ビルドとかいらない。故に工数がかなり減らせる。
 ・審査とか全くないので、不具合があればすぐに直せて反映できる。
 ・スマホ限定でなく、PC向けも同時開発できる。
 ・Webプログラマーであれば作れる。
●良くなかった点
 ・音の制御がいまいち。特にマナーモードでも鳴ってしまうのが最悪。
 ・画面を縦向き固定にしたかったのだが、できない。
 ・プログラムソースが丸見え。
 ・ブラウザによって、多少動きが違う。

まとめてみて思ったんだけど、やっぱりスマホアプリはXCodeやAndroidStudioでネイティブアプリを作るのが一番かな…。
だけど、工数が減らせるのはかなり魅力的!
あと、メンテナンス性も抜群なので、なしとは言い難い…。

悩ましいところだ。

とにかく少ない工数でできるので、もう一つアプリを作ってみました。
良かったら、こちらもお試しあれ!

みんな大好き!計算ドリルメーカー

くじ引きを作って遊ぼう!「みんな大好き!くじ引きメーカー」完成!

新年会、忘年会、歓迎会等々…
集って盛り上がりたい時に、役立つウェブアプリを作ってみました。

無料で公開してますので、ぜひお試しください。

みんな大好き!くじ引きメーカー

JavaScriptで配列をシャッフル

PHPには、shuffleという配列をシャッフルしてくれる関数があるけど、JavaScriptにはない。
なので、Fisher-Yates shuffleというアルゴリズムでシャッフルする関数を作ってみた。

/**
 * 関数:array_shuffle
 * 説明:引数の配列をシャッフルした結果を返す
 * 引数:
 *   array arr 配列
 * 戻り:
 *   array arr シャッフルした配列
 */
function array_shuffle(arr) {
    ret = arr.concat();
    for(var i = ret.length - 1; i > 0; i--){
        var r = Math.floor(Math.random() * (i + 1));
        var tmp = ret[i];
        ret[i] = ret[r];
        ret[r] = tmp;
    }
    return ret;
}

// array_shuffleを実行
var arr = [0,1,2,3,4,5,6,7,8,9];
var result = array_shuffle(arr);
console.log(arr);
console.log(result);

JavaScriptで文字数でなくてバイト数を求める

JavaScriptで、テキストの文字数でなくてバイト数を求める方法。

●まずは文字数
var str = 'This is a test. これはテストです。';
alert(str.length);
●こちらがバイト数
var str = 'This is a test. これはテストです。';
alert(encodeURIComponent(str).replace(/%../g,'c').length);
軽く解説:
encodeURIComponentは特定の文字をUTF8としてURLエンコードする関数。(%xxという形にしてくれる)
これを利用して、%xxを正規表現で'c'という文字に置き換えた文字列の文字数を計算するとバイト数になるというわけ。

jQueryでタグの階層

レンダリング後のタグの階層とその数を調べたかったので、作ってみました。

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
var myTag = {};
function myTags(elm, tg) {
	tg += (tg===''?'':'>')+$(elm).prop('tagName');
	if (typeof(myTag[tg])==='undefined') myTag[tg] = 0;
	myTag[tg]++;
	if ($(elm).children().length) {
		$(elm).children().each(function() {
			myTags(this, tg);
		});
	}
}
$(function() {
	var tg = '';
	myTags($('html'), tg);
	for (var tg in myTag) {
		console.log(tg + ':' + myTag[tg]);
	}
});
</script>

MySQL テーブルをそのままコピー

良く忘れるので、備忘録。

●テーブルレイアウトのコピー

●テーブルデータのコピー

【おまけ】指定のカラムだけデータをコピー

簡単なだけに、すぐ忘れてしまう…

ファイルの排他制御について

いつもシンボリックやディレクトリを作って排他制御をしてたんだけど、
phpにはflock()って関数があるので、調べてみました。

●まずは仕様

bool flock(resource handle, int operation)
ファイルをロックします。対象となるハンドルはfopenなどで取得したハンドルを指定します。

引数:
  handle  対象となるファイルのハンドル
  operation  ロックの方法
返り値:
  成功した場合に TRUE、失敗した場合に FALSE

◆ロックの方法は↓

説明
LOCK_SH 共有ロック
※書込みのみロック
LOCK_EX 排他的ロック
※読み書きロック
LOCK_UN ロック解除
ロックを解除する時に使う
LOCK_NB ロック中にflock()でブロックさせない
※単独で使わず、「LOCK_SH|LOCK_NB」や「LOCK_EX|LOCK_NB」のように”|”で繋いで使う

●実際に使ってみる

カウンターを作ってみます。
要件:
・実行のたびに、1づつ増える
・ロックのタイムアウトは5秒
<?php
// 変数
$counterFile = './count.dat'; // カウンターファイル
$timeout = 5; // ロックタイムアウト(秒)

// カウンターファイルがあるかフラグ
$counterExists = (file_exists($counterFile) && filesize($counterFile)>0);

// カウンターファイルをオープン
if ($fp=fopen($counterFile, ($counterExists?'r+':'w'))) {
    // ロック開始(タイムアウトあり)
    do {
        if (flock($fp, LOCK_EX|LOCK_NB)) { // ロック成功
            // ファイルポインタを先頭に移動
            rewind($fp);
            // ファイルから読込
            $count = $counterExists?fread($fp, filesize($counterFile)):0;
            // カウントをインクリメント
            ++$count;
            // ファイルを空にする
            ftruncate($fp, 0);
            // ファイルポインタを先頭に移動
            rewind($fp);
            // カウントをファイルに書き込み
            fwrite($fp, $count);
            // ロック解除
            flock($fp, LOCK_UN);
            break;
        }
        --$timeout;
        // ロックに失敗したので、まだタイムアウトでなければ1秒待つ
        if ($timeout>0) {
            sleep(1);
        }
    } while($timeout);
    // ファイルを閉じる
    fclose($fp);
}
?>

●使ってみた感想
悪くはないんだけど、今一つ使いにくい気がするなぁ~
ファイルポインタが引数なので、必ずファイルオープンしないといけなかったり、fseekしたりで面倒な感じ…。
もっと上手い使い方があるのかなぁ…。
これからもシンボリックリンクやディレクトリを使って排他制御するかぁ…。

ブログはじめました

どうもueachです。
ブログはじめました。

開発中に気になったこととか記事にしていこうと思っています。
主にPHPやJavaScriptのことが多いと思います。

間違ったことを記事にしてしまうかもしれないので、
ご覧になってお気付きのことがあればコメントください。

どうぞよろしくお願いいたします。