スキップしてメイン コンテンツに移動

ActionScript 3.0: Hit Youtube API

I wrote a tiny program for getting Youtube video information by hitting Youtube API in ActionScript 3.0.
Some generic classes or methods might be missing but I think you can easily guess what they are doing and can add them easily yourself.

Example Usage

Ok, starting from the example usage.
package utils.video 
{
    import flash.display.Sprite;
    import utils.file.SyncFileSaveDownLoader;
    import utils.ITaskProgressCounter;
    import utils.TaskProgressCounter;
    import utils.video.youtube.YoutubeAPI;
    import utils.video.YoutubeFLVURLGetEvent;
    import utils.video.youtube.YoutubeLocalCacheManager;
    import utils.video.youtube.YoutubeVideoEntryDispatcher;
    import utils.video.youtube.YoutubeVideoEntryEvent;

    public class Tester extends Sprite
    {
        private    var downloader:SyncFileSaveDownLoader = new SyncFileSaveDownLoader();
        private var youtubeLocalCacheManager:YoutubeLocalCacheManager =  new YoutubeLocalCacheManager("c:\\temp\\youtube", downloader);
        
        public function Tester() 
        {
            var dispatcher:YoutubeVideoEntryDispatcher = new YoutubeVideoEntryDispatcher();
            dispatcher.addEventListener(YoutubeVideoEntryEvent.CREATED, created);
            
            var api:YoutubeAPI = new YoutubeAPI("utf-8", dispatcher);
            
            api.searchByKeywords(10, "Google IO");
            
            // directtly hitting api
            // api.exec("http://gdata.youtube.com/feeds/videos?start-index=1&max-results=25", 2);
            // api.exec("http://gdata.youtube.com/feeds/users/youtube/uploads?start-index=1&max-results=25", 2);
        }
        
        public function created(evt:YoutubeVideoEntryEvent):void {
             // debug print
            trace("---");
            trace(evt.videoEntry.author.name);
            trace(evt.videoEntry.playerURL);
            
            // download thumbnails
            youtubeLocalCacheManager.downloadThumbnails(evt.videoEntry);
        }
   }
    
}


Main Classes

package utils.video.youtube
{
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.net.URLRequest;
    import flash.events.ProgressEvent;
    import flash.utils.ByteArray;
    import utils.StringUtils;
    import utils.tool.ByteLoadHelper;
    import utils.tool.ByteLoadEvent;

    public class YoutubeAPI
    {
        private var charEncode:String;
        private var videoParser:YoutubeVideoEntryParser;
            
        private var baseURL:String = "http://gdata.youtube.com/feeds/videos/";
        
        public static const NS_ATOM:Namespace = new Namespace("", "http://www.w3.org/2005/Atom");
        public static const NS_MEDIA:Namespace = new Namespace("media","http://search.yahoo.com/mrss/");
        public static const NS_GD:Namespace = new Namespace("gd","http://schemas.google.com/g/2005");
        public static const NS_YT:Namespace = new Namespace("yt","http://gdata.youtube.com/schemas/2007");
        public static const Q_ENTRY:QName = new QName(NS_ATOM, "entry");    
        public static const Q_LINK:QName = new QName(NS_ATOM, "link");
        
        public function YoutubeAPI(charEncode:String, videoParser:YoutubeVideoEntryParser) 
        {
            this.charEncode = charEncode;
            this.videoParser = videoParser;
        }
        
        public function searchByKeywords(max:int, ...args):void {
            var queryStr:String = StringUtils.concatAllAsString("+", args);
            if(queryStr.length > 0){
                exec(baseURL + "?vq=" + queryStr, max);
            }
        }
        
        public final function exec(url:String, max:int):void
        {
            var loader:ByteLoadHelper = new ByteLoadHelper();
            loader.addEventListener(ByteLoadEvent.COMPLETE, completeHandler);
            loader.load(url);
            
            var maxInit:uint = max;

            function completeHandler(evt:ByteLoadEvent):void {
                loader.removeEventListener(ByteLoadEvent.COMPLETE, completeHandler);
                var bytes:ByteArray = evt.data;
                var rss:String = bytes.readMultiByte(bytes.bytesAvailable, charEncode);
                var xml:XML = new XML(rss);
                
                for each(var entryXML:XML in xml.child(Q_ENTRY)) {
                    if (max <= 0) {
                        return;
                    }
                    
                    try {
                        videoParser.parse(entryXML);
                    }
                    catch (e:TypeError) {
                        // should so proper error handling
                        trace(e);
                    }
                    max--;
                }
                
                if(maxInit > 0){
                    var linkNext:XMLList = xml.child(Q_LINK).(attribute("rel") == "next");
                    for each(var elemXML:XML in linkNext) {
                        exec(elemXML.attribute("href"), maxInit);
                    }
                }
            }
        }
    }
    
}
package utils.video.youtube 
{
    import flash.events.EventDispatcher;

    public class YoutubeVideoEntryDispatcher extends EventDispatcher implements YoutubeVideoEntryParser
    {
        private var factory:YoutubeVideoEntryFactory;
        
        public function YoutubeVideoEntryDispatcher() 
        {
            this.factory = new YoutubeVideoEntryFactory();
        }
    
        public function parse(entryXML:XML):void {
            dispatchEvent(new YoutubeVideoEntryEvent(factory.create(entryXML), YoutubeVideoEntryEvent.CREATED));
        }
    }
    
}
package utils.video.youtube 
{
    import utils.StringUtils;
    import utils.video.ThumbnailInfo;
    
    public class YoutubeVideoEntryFactory
    {
        
        public function YoutubeVideoEntryFactory() 
        {
            
        }
        
        public function create(entryXML:XML):YoutubeVideoEntry {
            var entry:YoutubeVideoEntry = new YoutubeVideoEntry();
            
            var authorList:XMLList = entryXML.child(new QName(YoutubeAPI.NS_ATOM, "author"));
            entry.author = new YoutubeAuthor(
                authorList.child(new QName(YoutubeAPI.NS_ATOM, "name")),
                authorList.child(new QName(YoutubeAPI.NS_ATOM, "uri"))
            );

            var idList:XMLList = entryXML.child(new QName(YoutubeAPI.NS_ATOM, "id"));
            var idURL:String = idList.toString();
            var id:String = StringUtils.extractLast(idURL, "/");
            entry.id = id;
            
            var mediaGroupList:XMLList = entryXML.child(new QName(YoutubeAPI.NS_MEDIA, "group"));
            for each(var mediaGroup:XML in mediaGroupList) {
                for each(var elemXML:XML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "player")).attribute("url")) {
                    entry.playerURL = elemXML.toString();
                }

                for each(elemXML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "keywords"))) {
                    var keywords:String = elemXML.toString();
                    var tags:Array = keywords.split(",");
                    for each(var tag:String in tags ) {
                        entry.tags.push(StringUtils.trim(tag));
                    }
                }
                
                for each(elemXML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "title"))) {
                    entry.title = elemXML.toString();
                }

                var categoryList:XMLList = mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "category")).(hasOwnProperty("@label"));
                for each(elemXML in categoryList) {
                    entry.category = elemXML.toString();
                }

                for each(elemXML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "description"))) {
                    entry.description = elemXML.toString();
                }

                for each(elemXML in mediaGroup.child(new QName(YoutubeAPI.NS_MEDIA, "thumbnail"))) {
                    entry.thumbnails.push(new ThumbnailInfo(
                        id,
                        elemXML.attribute("url").toString(), 
                        int(elemXML.attribute("width")), 
                        int(elemXML.attribute("height").toString()), 
                        decodeTimeStrToSec(elemXML.attribute("time").toString())
                    ));
                }
            }
                                    
            for each(elemXML in entryXML.child(new QName(YoutubeAPI.NS_GD, "rating"))) {
                entry.rating = new YoutubeRating(
                    int(elemXML.attribute("min")), 
                    int(elemXML.attribute("max")), 
                    int(elemXML.attribute("numRaters")), 
                    Number(elemXML.attribute("average"))
                );
            }
            
            for each(elemXML in entryXML.child(new QName(YoutubeAPI.NS_YT, "statistics"))) {
                entry.statistics = new YoutubeStatistics(
                    int(elemXML.attribute("viewCount")), 
                    int(elemXML.attribute("favoriteCount"))
                );
            }
            return entry;
        }
            
        public static function decodeTimeStrToSec(time:String):uint {
            var splitted:Array = time.split(":");
            return uint(splitted[0]) * 3600 + uint(splitted[1]) * 60 + uint(splitted[2]);
        }
    }

    
}
package utils.video.youtube 
{
    import flash.filesystem.File;
    import utils.file.IDownloader;
    import utils.Utils;
    import utils.video.IFlvUrlGetter;
    import utils.video.ThumbnailInfo;
    
    public class YoutubeLocalCacheManager 
    {
        private var _downloader:IDownloader;
        private var _baseDir:String 
        
        public function YoutubeLocalCacheManager(baseDir:String, downloader:IDownloader) 
        {
            this._downloader = downloader;
            this._baseDir = baseDir;
        }
        
        protected function getDir(entry:YoutubeVideoEntry):File {
            var id:String = entry.id;
            var dir:File = new File(_baseDir + File.separator + id);
            if (!dir.exists) {
                dir.createDirectory();
            }
            return dir;
        }
        
        
        public function downloadThumbnails(youtubeEntry:YoutubeVideoEntry):void {
            for each(var thumb:ThumbnailInfo in youtubeEntry.thumbnails) {
                var fileName:String = Utils.getFileName(thumb.url, "/");
                var path:String = getDir(youtubeEntry).nativePath + File.separator + fileName;
                if(!new File(path).exists){
                    _downloader.download(thumb.url, path);
                }
            }
        }
    }
    
}


Event Classes

package utils.video.youtube 
{
    import flash.events.Event;
    
    public class YoutubeVideoEntryEvent extends Event 
    {
        public static const CREATED:String = "youutbe_video_entry_created";
        
        private var _videoEntry:YoutubeVideoEntry;
        
        public function YoutubeVideoEntryEvent(videoEntry:YoutubeVideoEntry, type:String, bubbles:Boolean=false, cancelable:Boolean=false) 
        { 
            super(type, bubbles, cancelable);
            this._videoEntry = videoEntry;
            
        } 
        
        public override function clone():Event 
        { 
            return new YoutubeVideoEntryEvent(_videoEntry, type, bubbles, cancelable);
        } 
        
        public override function toString():String 
        { 
            return formatToString("YoutubeVideoEntryEvents", "type", "bubbles", "cancelable", "eventPhase"); 
        }
        
        public function get videoEntry():YoutubeVideoEntry {
            return _videoEntry;
        }
        
    }
    
}


Data Object Classes

Nothing special... :) simply flattened xml object to classes (we can able to use more common library to mapping between class and xml).
package utils.video.youtube
{
    import utils.TaggableEntry;
    import utils.video.ThumbnailInfo;

    public class YoutubeVideoEntry
    {
        private var _tags:Vector.<String> = new Vector.<String>();

        private var _id:String;
        private var _playerURL:String;
        private var _thumbnails:Vector.<ThumbnailInfo>;
        private var _rating:YoutubeRating;
        private var _description:String;
        private var _statistics:YoutubeStatistics;
        private var _author:YoutubeAuthor;
        private var _category:String;
        private var _title:String;
        
        public function YoutubeVideoEntry() 
        {
            this._thumbnails = new Vector.<ThumbnailInfo>();
        }
        
        public function set id(id:String):void {
            this._id = id;
        }

        public function get id():String {
            return _id;
        }

        public function set playerURL(url:String):void {
            this._playerURL = url;
        }
        
        public function get playerURL():String {
            return _playerURL;
        }
        
        public function get thumbnails():Vector.<ThumbnailInfo> {
            return _thumbnails;
        }
        
        public function set rating(rate:YoutubeRating):void {
            this._rating = rate;
        }

        public function get rating():YoutubeRating {
            return _rating;
        }

        public function set description(description:String):void {
            this._description = description;
        }
        
        public function get description():String {
            return _description;
        }
        
        public function set statistics(statistics:YoutubeStatistics):void {
            this._statistics = statistics;
        }
        
        public function get statistics():YoutubeStatistics {
            return this._statistics;
        }
        
        public function set author(author:YoutubeAuthor):void {
            this._author = author;
        }
        
        public function get author():YoutubeAuthor {
            return this._author;
        }
        
        public function set category(category:String):void {
            this._category = category;
        }
        
        public function get category():String {
            return this._category;
        }
        
        public function set title(title:String):void {
            this._title = title;
        }
        
        public function get title():String {
            return this._title;
        }
        
        public function get key():String {
            return id;
        }
            
        public function get tags():Vector.<String> {
            return _tags;
        }    
    }
    
}
package utils.video.youtube
{

    public class YoutubeAuthor 
    {
        
        private var _name:String;
        private var _uri:String;
        
        public function YoutubeAuthor(name:String, uri:String) 
        {
            this._name = name;
            this._uri = uri;
        }
        
        public function get name():String {
            return _name;
        }

        public function get uri():String {
            return _uri;
        }
        
    }
    
}
package utils.video.youtube
{
    public class YoutubeRating 
    {
        private var _max:int, _min:int, _numRaters:int, _average:Number;
        
        public function YoutubeRating(max:int, min:int, numRaters:int, average:Number) 
        {
            this._max = max;
            this._min = min;
            this._numRaters = numRaters;
            this._average = average;
        }
        
        public function get max():int {
            return _max;
        }

        public function get min():int {
            return _min;
        }

        public function get numRaters():int {
            return _numRaters;
        }


        public function get average():Number {
            return _average;
        }
        
    }
    
}
package utils.video.youtube
{
    public class YoutubeStatistics 
    {
        private var _viewCount:int;
        private var _favoriteCount:int;
        
        public function YoutubeStatistics(viewCount:int, favoriteCount:int) 
        {
            this._viewCount = viewCount;
            this._favoriteCount = favoriteCount;
        }
        
        public function get favoriteCount():int
        {
            return _favoriteCount;
        }
        
        public function get viewCount():int {
            return _viewCount;
        }
        
    }
    
}


Helper Classes

Mainly class for downloading resources.
You can downsize this class if you need. I think a bit too much for this youtube example :P
package utils.file
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import utils.IOUtils;
    
    public class SyncFileSaveDownLoader extends EventDispatcher implements IDownloader
    {
        private var id:int;
        
        public function SyncFileSaveDownLoader() 
        {
            this.id = 0;
        }
        
        public function download(url:String, path:String, listenProgress:Boolean=false):FileDownloadTask {
            var loader:URLLoader = new URLLoader();
            var task:FileDownloadTask = new FileDownloadTask(id.toString(), url, path, loader);
            
            loader.dataFormat = URLLoaderDataFormat.BINARY;
            loader.addEventListener(Event.COMPLETE, completeHandler);
            loader.addEventListener(IOErrorEvent.IO_ERROR, ioEllorHandler);
            if(listenProgress){
                loader.addEventListener(ProgressEvent.PROGRESS, handleProgress);
            }
            loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            loader.load(new URLRequest(url));
            id++;
            return task;
            
            function handleProgress( pevt:ProgressEvent ):void {
                var total:Number = pevt.bytesTotal;
                if (total != 0) {
                    task.totalBytes = total;
                    task.percentage = Math.round( pevt.bytesLoaded / total * 100 );
                    task.status = FileDownloadTask.DOWNLOADING;
                }
            }
            
            function completeHandler(cevt:Event):void
            {
                saveByteData(cevt.target.data, path);
                task.status = FileDownloadTask.SUCCESS;
                finalize();
            }

                    
            function securityErrorHandler(e:SecurityErrorEvent):void 
            {
                task.status = FileDownloadTask.FAIL;
                finalize();
            }
            
            function ioEllorHandler(e:IOErrorEvent):void 
            {
                task.status = FileDownloadTask.FAIL;
                finalize();
            }
            
            function finalize():void {
                loader.removeEventListener(Event.COMPLETE, completeHandler);
                loader.removeEventListener(ProgressEvent.PROGRESS, handleProgress);
                loader.removeEventListener(IOErrorEvent.IO_ERROR, ioEllorHandler);
                loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
                task.dispose();
            }

        }

        public static function saveByteData(data:ByteArray, path:String):void {
            try {
                var file:File = new File(path);
                var fs:FileStream = new FileStream();
                fs.open(file, FileMode.WRITE);
                fs.writeBytes(data);
                fs.close();
            }
            catch (err:IOError) {
                trace(err);
            }
        }
    }
    
}
package utils.file 
{
    import flash.events.Event;
    
    public class FileDownloadEvent extends Event 
    {
        public static const SAVE_CONPLETE:String = "SaveComplete";
        
        private var _url:String;
        private var _path:String;
        
        public function FileDownloadEvent(url:String, path:String, type:String, bubbles:Boolean=false, cancelable:Boolean=false) 
        { 
            super(type, bubbles, cancelable);
            this._url = url;
            this._path = path;
        } 
        
        public override function clone():Event 
        { 
            return new FileDownloadEvent(_url, _path, type, bubbles, cancelable);
        } 
        
        public override function toString():String 
        { 
            return formatToString("FileDownloadEvent", "type", "bubbles", "cancelable", "eventPhase"); 
        }
        
        public function get url():String {
            return _url;
        }
        
        public function get path():String {
            return _path;
        }
    }
    
}
package utils.file 
{
    import flash.net.URLLoader;
    
    public class FileDownloadTask
    {
        public static const DOWNLOADING:String = "Downloading";
        public static const SAVING:String = "Saving";
        public static const SUCCESS:String = "Success";
        public static const FAIL:String = "Fail";
        
        private var _id:String;
        private var _url:String;
        private var _path:String;
        private var _status:String;
        
        private var _percentage:Number;
        private var _totalBytes:Number;
        private var _urlLoader:URLLoader;
        
        public function FileDownloadTask(id:String, url:String, path:String, urlLoader:URLLoader) 
        {
            this._id = id;
            this._url = url;
            this._path = path;
            this._urlLoader = urlLoader;
        }
        
        public function get url():String { return _url; }
        
        public function get id():String { return _id; }
        
        public function get path():String { return _path; }
        
        public function set status(status:String):void {
            if (status != DOWNLOADING && status != SUCCESS && status != FAIL && status != SAVING) {
                throw new Error("Invalid Status");
            }
            this._status = status;
        }
        
        public function get status():String {
            return _status;
        }
        
        public function cancel():void {
            if (_urlLoader != null) {
                _urlLoader.close();
                _urlLoader = null;
            }
        }
        
        public function dispose():void {
            _urlLoader = null;
        }
        
        public function get percentage():Number { return _percentage; }
        
        public function set percentage(value:Number):void 
        {
            _percentage = value;
        }
        
        public function get totalBytes():Number { return _totalBytes; }
        
        public function set totalBytes(value:Number):void 
        {
            _totalBytes = value;
        }
        
    }
    
}

コメント

このブログの人気の投稿

Eclipseでコードカバレッジのハイライトを削除する方法

Eclipseには便利なコードカバレッジ表示機能が搭載されていますが、コード内に緑、赤、黄の色付けがされて煩く感じるときもあると思います。 1度カバレッジの色付けが出てしまった後に消す方法の紹介です(方法は簡単)。 下記のキャプチャの青いマーカーで示した「Remove All Sessions」のボタンを押せばすべて消えます。

「特定の文字から始まらない文字列」にマッチする正規表現

「特定の文字から始まらない文字列」 にマッチする正規表現の例です。  以下の例では、Aから始まらない文字列にマッチする正規表現を示しています。 ^(?!A).*$ 私も正規表現の組み方で四苦八苦することがあります。以下の書籍は実践的に様々な正規表現のパターンを例示してくれているので、重宝しています。

ダイソーで買った200円のドライバーセットでHDDを分解

HDDの処分 最近は個人情報の問題もあって、HDDを処分する前にちゃんとデータの消去を気にすることも多くなってきました。消去方法としては大きく分けて下記の3つがあります。 データ消去ソフトでフォーマット HDD内部のプラッタを物理破壊 データ消去を行ってくれる専門の業者や家電量販店(Sofmapやビックカメラで実施していると思います。費用発生。)に持ち込み。 データ消去ソフトでのフォーマットは簡単ですが、欠点として「フォーマットに時間がかかる」「セクタ破損などで中途半端に壊れたディスクのフォーマットができない」などがあります。 またHDD内部のプラッタの物理破壊については、HDDを分解するために、通常のプラスやマイナスドライバーではなく、星形ネジに対応したトルクスドライバーが必要とのこともあって、少し面倒です。 筆者は今回、今後もHDDの廃棄をするだろうなあと思い、思い切って自分で分解して廃棄することにチャレンジしてみました。(家電量販店に持って行くよりも安くできないかというどケチ丸出しですw) HDDの星形ネジ こんなやつです。ちなみに写真はSeagateのST2000DL003というHDDで撮影しました。 トルクスドライバー というわけで、分解のために Amazonでトルクスドライバー を探しました。 調べると T8のもだと使えそう とのことで、いろいろと物色。 セットのものとか T8一本で立派なやつとか 色々あったのですが、HDD壊すだけで800円かぁ(←どケチ)、と思って購入を躊躇。 ネット上で調べると100円ショップのダイソーでも、トルクスドライバーを販売しているとの情報をキャッチ!近所のダイソーに行って、探したところ星形のヘッド交換に対応した精密ドライバーセットがありました。 プラスが10種類、マイナスが8種類、六角が6種類、星形が6種類(今回ほしかったもの)のセットで、何とお値段税抜き200円!、税抜き200円!と安かったので、ダメもとで購入しました。 結論から言うと 買って大正解 でした。 ダイソーの精密ドライバーセット こんな商品です! 星形対応のヘッドを装着するとこんな感じ。ドライバーのグリップもゴムで滑らない様になっていて使いやす...

SQLで特定の文字を組み合わせたランダムな文字列を生成

簡易的な方法として「指定した文字列からランダムに1文字選ぶ」を必要な文字の長さ分concat関数でつなげれば実現できます。 1文字ずつ文字を選ぶので、あまり性能もよくない上、セキュリティ的な観点からのランダム性も担保されていないので、あくまで開発中に必要になった時に使う程度が無難だと思います。 下記に英数字大文字小文字を含んだランダムな3文字の文字列を生成するクエリを示します。 # RAND関数で指定した文字列からランダムに1文字選択。 # 下記の例の62の部分はa~z、A~Z、1~9の文字数の合計値を入れた結果 SELECT CONCAT( SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1), SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1), SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1) ) AS random_string;

PHPの配列(array)のメモリ使用量の考察

はじめに 最近PHP上に大量のデータをメモリ上に展開していたのですが、配列(array)の形式(連想配列 or 単純な配列)や配列の要素のデータ構造(数字、配列、文字列など)で大きくメモリ使用量に差が出てくることに気づき、簡単なプログラムを組んで調べてみました。 あくまで筆者の環境での結果なので、細かい数値は参考程度に見てください。 測定環境と方法 OS: Windows 10 PHP 7.4.5 (php-7.4.5-nts-Win32-vc15-x64) 配列に要素を追加するプログラムを書いて、PHPのmemory_get_usage(true)関数を使って実メモリ使用量を計測しました。 計測結果 No. 方式 1MB当たり作成できる 要素数 プログラム 補足 1 キーも値も整数の配列 (整数IDを想定) 28571 // 2,000,000 / 70MB $row = []; for($i = 0; $i < 2000000; $i++) { $row[] = $i; } No.2~6でテストしたプログラム中の要素数は200,000。これだけ一桁多い! 2 キーが文字列、値が整数の連想配列 8333 // 200,000 / 24MB $row = []; for($i = 0; $i < 200000; $i++) { $row[$i.'_key_string'] = $i; } キーの文字列が長い方がメモリ使用量多くなる。 3 キーが整数、値が連想配列の配列 DBから取得してきたデータを想定 2325 // 200,000 / 86MB $row = []; for($i = 0; $i < 200000; $i++) { row[] = ['id' => $i]; } 4 キーが整数、値が連想配列の配列(配列に複数の値を保持) DBから取得してきたデータを想定 2127 // 200,000 /...

ADODB.streamオブジェクトを使って文字列とByte配列を相互変換(Excel VBA)

ADODB.streamオブジェクトを使って文字列をByte配列に変換するコードのサンプルです。 ExcelVBAでADODB.streamを使う際には、 1. ExcelのMicrosoft Visual Basic エディタのメニューバーから「ツール->参照設定」とたどる。 2. 表示されたダイアログからMicrosoft ActiveX Data Objectsにチェックを入れる。 という手順が必要です。 文字列からByte配列へ Private Function ADOS_EncodeStringToByte(ByVal cset As String, ByRef strUni As String) As Byte() On Error GoTo e Dim objStm As ADODB.stream: Set objStm = New ADODB.stream objStm.Mode = adModeReadWrite objStm.Open objStm.Type = adTypeText objStm.Charset = cset objStm.WriteText strUni objStm.Position = 0 objStm.Type = adTypeBinary Select Case UCase(cset) Case "UNICODE", "UTF-16" objStm.Position = 2 Case "UTF-8" objStm.Position = 3 End Select ADOS_EncodeStringToByte = objStm.Read() objStm.Close Set objStm = Nothing Exit Function e: Debug.Print "Error occurred while encoding characters" & Err.Description If objStm Is No...

MySQL: SELECTの結果をUNIONして ORDER BYする際の最適化方法

SELECTの結果をUNIONして ORDER BY する際には下記の点に注意する必要があります。 無駄なメモリ消費 ソートにINDEXが利かない (≒CPU負荷増大) 対応策 可能であればPush-down Condition (各サブクエリ内でORDER BY, LIMIT, OFFSETを適用してからUNION, ORDER BYを実行する)を利用することで、 パフォーマンスを改善できる場合があります。 下記に例を示します。 もともとのクエリ SELECT tmp.* FROM ( SELECT tableA.column1, tableA.column2 FROM tableA WHERE (条件) UNION ALL SELECT tableB.column1, tableB.column2 FROM tableB WHERE (条件) ) AS tmp ORDER BY tmp.column1, tmp.column2 LIMIT 100, 20 Push-down Conditionを用いて書き直したクエリ SELECT tmp.* FROM ( SELECT tableA.column1, tableA.column2 FROM tableA WHERE (条件) ORDER BY tableA.column1, tableA.column2 LIMIT 30 # ただしこのPush-down Conditionの手法も下記の場合は、効果が半減しますので注意が必要です。 OFFSETの値が大きい場合は、結局全結果セットUNIONと変わらない サブクエリ内のソートで、INDEXが効かない場合

Visual Studio 2010 SP1のアンインストール

Visual Studio 2013に乗り換えるためにVisual Studio 2010をアンインストールしようとしたところで問題発生。。。 先にVisual Studio 2010本体をアンインストールした後、Visual Studio 2010 SP1をアンインストールできなくて困っていました。 Google先生で調べたところ、以下の情報が見つかり、書かれていた通り実施したところ無事Visual Studio 2010 SP1のアンインストールに成功しました。 How to uninstall/remove Visual Studio SP1 アンインストール手順は以下の通りです。 http://www.microsoft.com/en-gb/download/details.aspx?id=23691 からMicrosoft Visual Studio 2010 Service Pack 1 (Installer)をダウンロード VS10sp1-KB983509.exeというファイル名でダウンロードされる(はず)。 コマンドプロンプトから以下のコマンドを実行 (以下の例は、c:\tempにVS10sp1-KB983509.exeがある場合) c:\temp\VS10sp1-KB983509.exe /uninstall /force ダイアログが立ち上がるので、アンインストールを選択して次へ進めばOK!

PHPでファイルを指定した行数ごとに分割

ファイルを指定した行数ごとに分割するためには、Linuxのsplitコマンドを使えば簡単に実現できます。 PHPではexec関数にsplitコマンドを渡して実行すればよいですが、下記の弱点があります。 Linuxのコマンドに依存 (PHPの場合はほとんどLinux環境で動作させることが普通なのでそこまで問題にならないかも知れません)。 exec関数は慎重に引数を渡さないと、OSコマンドインジェクション脆弱性を引き起こす可能性がある。 そこで、今回はPHPでファイルを指定した行数ごとに分割するプログラムを書いてみました。 <?php class FileSplitter { private $lines; private $fileCount; public function split($filePath, $linesPerFile, $outputDir) { $this->fileCount = 0; $this->lines = null; $file = new \SplFileObject($filePath); $lineCount = 0; try{ while (!$file->eof()) { if($lineCount % $linesPerFile === 0) { $this->writeToFile($this->generateOutputFilePath($outputDir, $file)); } $this->lines[] = $file->fgets(); $lineCount++; } $this->writeToFile($this->generateOutpu...

MySQLのSQL_CALC_FOUND_ROWS使用上の注意

MySQLのSQL_CALC_FOUND_ROWSを使用する際の無駄なメモリ消費に注意 ページング機能の実装などのために、ヒットした全件数を取得する必要のある場合があるかと思います。 その場合、下記のようにSQL_CALC_FOUND_ROWSを使うと、検索結果に加えて、そのクエリの全ヒット件数が取得できます。 SELECT SQL_CALC_FOUND_ROWS table.column1, table.column2 ...(途中省略)... FROM table WHERE (条件) しかし、SQL_CALC_FOUND_ROWSを使うと、「絞り込んだ結果にヒットする全べての行の結果のセットを作成する」という大きな欠点があります。 これは、LIMIT, OFFSETを指定していても実行されてしまうので、 SELECTで指定するカラム数やデータ量が多い SELECTの結果返ってくる行数が多い 場合、無駄に領域(≒メモリ)を消費してしまうので注意が必要です。 例えば、100万行検索対象としてヒットするクエリの場合、仮にLIMIT 20と指定して最初の20行を取得するようにクエリを書いても、その100万行分の結果セットが作成されてしまうということになります。 対応策 根本的な対応策としては、SQL_CALC_FOUND_ROWSを使わない(結果行数の取得はCOUNTを用いた別クエリで取得する、結果行数をあらかじめサマリーテーブルに保持しておく)ことですが、 SQL_CALC_FOUND_ROWSをどうしても使う必要がある場合は、 SELECT SQL_CALC_FOUND_ROWS primary_id のように最低限のカラムを指定して結果行セットを取得 (LIMIT OFFSET指定前提) 取得したprimary_idを使って必要なデータを取得 して、SQL_CALC_FOUND_ROWSで使用する領域をできるだけ減らすことで、対応するしかないと思います。 世の中ではLIMIT, OFFSETは使わない方がよいとよく書かれていますが、 SQL_CALC_FOUND_ROWSは、書いてしまえばどんなときも検索にヒットする全結果行セットを作成するので、同じくらい使用する際には注意が必要です。