質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

4回答

1919閲覧

fromCharCode.applyの引数の制限を回避する方法

terater

総合スコア9

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2017/09/21 20:58

###前提・実現したいこと
元々がカンマ区切りのデータを引数にとって
String.fromCharCode.applyで処理をしている箇所があります。
ファイルのデータが少ない際には問題ないのですが、
一定の容量になるとエラーになります。

原因としてはapplyの引数に渡せる個数に制限があるため、
一定容量を超えると使えなくなるようでした。

この制限を回避する方法はないでしょうか。

###試したこと
考えた事として制限としてかかっている数に分割して、
後に結合すればよいと単純に思いました。
ただしそのような処理をして正常な状態に戻るのかという疑問と、
バイナリデータ操作する方法が根本的によく分かりませんでした。

###発生している問題・エラーメッセージ

SCRIPT XX: スタック領域が不足しています。

###該当のソースコード

JavaScript

1var input = String.fromCharCode.apply(null, this.sampledata)

###補足情報(言語/FW/ツール等のバージョンなど)
ブラウザIE11

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/09/21 22:06

引数がおおいんじゃなくデータ量が多いのだけどね(メモリ不足)
think49

2017/09/21 23:51

asahina1979さん、あまり詳しくないのですが、スタックオーバーフローはスタック領域を超過した際に起きるものと理解しています。ざっくり、ググった限りでは、スタック領域はプログラム側で定めた固定サイズで確保するように読めました。fromCharCodeに限っては、空きメモリ容量に応じたスタック領域を動的に確保するように実装されているのでしょうか。 http://www.kab-studio.biz/Programing/JavaA2Z/Word/00000987.html
Lhankor_Mhy

2017/09/22 05:58

手元のFirefoxで確認しましたら500000が上限でした。2^nでもない不自然にキリのいい数字なので、実装上の制限のような気がしますね。
guest

回答4

0

環境にもよるのかもしれませんが、ArrayBufferBlobとして、FileReaderAPIを使ったらエラーになりませんでした。

javascript

1var sampledata= Array(1000000).fill(97); 2//String.fromCharCode.apply(null, sampledata); ←これはエラー。 3var blob = new Blob( [new Uint8Array(sampledata).buffer] ); 4var reader = new FileReader(); 5reader.onload = function(e){ 6 console.log(reader.result); 7}; 8reader.readAsText(blob);

でも、ウチのFirefoxさんは10万字に耐えたので現実的にはfromCharCodeで十分な気も……

投稿2017/09/22 04:58

Lhankor_Mhy

総合スコア36115

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

0

引数の数の制限についてStack Overflowに質問があったので紹介します。

Is there a max number of arguments JavaScript functions can accept?

上記質問の各回答によると、仕様としては制限はありません(ES5.1で述べていますが、最新のES8の6.2.1The List and Record Specification Typesも同じ文言です)。しかし、各ブラウザ毎に実装上の制限は存在するようで、その制限値もブラウザによって異なります。また、スタック不足のエラーメッセージとなっていますが、単なるスタックオーバーフローであるとか、そう単純な話でも無いようです。

とりあえず、制限はあります。32767以下でないと安全とは言えないでしょう。では、バラバラに処理して、文字列を結合させて問題ないかというと問題ありません。下記のテスト用コードを実行してみてください。

JavaScript

1"use strict"; 2function stringToCharCodeList(str) { 3 return Array.prototype.map.call(str, function(c) { 4 return c.charCodeAt(0); 5 }); 6} 7 8function charCodeListToString(list) { 9 return String.fromCharCode.apply(null, list); 10} 11 12function charCodeListToStringSplit(list) { 13 return list.reduce(function(str, code) { 14 return str + String.fromCharCode(code); 15 }, ""); 16} 17 18function charCodeListToStringSplitFor(list) { 19 var str = ""; 20 for (var i = 0, len = list.length; i < len; i++) { 21 str += String.fromCharCode(list[i]); 22 } 23 return str; 24} 25 26function charCodeListToStringSlice(list, sliceSize) { 27 var str = ""; 28 for (var i = 0, len = list.length; i < len; i += sliceSize) { 29 var slicedList = list.slice(i, i + sliceSize); 30 str += String.fromCharCode.apply(null, slicedList); 31 } 32 return str; 33} 34 35function checkConvert(str) { 36 var list = stringToCharCodeList(str); 37 var convertedStr = charCodeListToString(list); 38 var splitConvertedStr = charCodeListToStringSplit(list); 39 var splitForConvertedStr = charCodeListToStringSplitFor(list); 40 var sliceConvertedStr = charCodeListToStringSlice(list, 8); 41 console.log(str + 42 "\n list: [" + list + "]" + 43 "\n bulk: " + convertedStr + 44 "\n split: " + splitConvertedStr + 45 "\n spfor: " + splitForConvertedStr + 46 "\n slice: " + sliceConvertedStr); 47} 48 49checkConvert("Hello, World!"); 50checkConvert("こんにちは、世界!"); 51checkConvert("昨日、近所の????野家行ったんです。????野家。"); 52checkConvert("????帰ったら????くれ");

charCodeListToStringSplit()が一個一個処理するバージョン、charCodeListToStringSplitForforを使った別実装、charCodeListToStringSlice()は指定のサイズにまとめて処理するバージョンです。どちらも正常に動くことが確認できると思います。なお、"????"や"????"のようなU+10000以上になるためサロゲートペアで構成される文字が含まれる場合、途中不正な文字列が生成されることに注意してください。その後結合すれば問題はありません。

では、パフォーマンスはどうなのかです。ということでベンチマークテストを作りました。Benchmark.jsを入れてから試して見てください。

"use strict"; var Benchmark = require('benchmark') function charCodeListToString(list) { return String.fromCharCode.apply(null, list); } function charCodeListToStringSplit(list) { return list.reduce(function(str, code) { return str + String.fromCharCode(code); }, ""); } function charCodeListToStringSplitFor(list) { var str = ""; for (var i = 0, len = list.length; i < len; i++) { str += String.fromCharCode(list[i]); } return str; } function charCodeListToStringSlice(list, sliceSize) { var str = ""; for (var i = 0, len = list.length; i < len; i += sliceSize) { var slicedList = list.slice(i, i + sliceSize); str += String.fromCharCode.apply(null, slicedList); } return str; } var maxCharCode = 65535 // 2**16 - 1 var listSize = 32767; var list = []; for (var i = 0; i < listSize; i++) { list.push(Math.floor(Math.random() * (maxCharCode + 1))); } var suite = new Benchmark.Suite; suite .add("Bulk", function() { charCodeListToString(list); }) .add("Split", function() { charCodeListToStringSplit(list); }) .add("Split For", function() { charCodeListToStringSplitFor(list); }) .add("Slice 1", function() { charCodeListToStringSlice(list, 1); }) .add("Slice 8", function() { charCodeListToStringSlice(list, 8); }) .add("Slice 64", function() { charCodeListToStringSlice(list, 64); }) .add("Slice 256", function() { charCodeListToStringSlice(list, 256); }) .add("Slice 1024", function() { charCodeListToStringSlice(list, 1024); }) .add("Slice 4096", function() { charCodeListToStringSlice(list, 4096); }) .add("Slice 16384", function() { charCodeListToStringSlice(list, 16384); }) .add("Slice 32767", function() { charCodeListToStringSlice(list, 32767); }) .on('cycle', function(event) { console.log(String(event.target)); }) .run({ 'async': true });

手元のNode.jsでは次の結果になりました。

Bulk x 7,640 ops/sec ±3.31% (79 runs sampled) Split x 505 ops/sec ±2.49% (76 runs sampled) Split For x 502 ops/sec ±2.55% (75 runs sampled) Slice 1 x 119 ops/sec ±2.77% (65 runs sampled) Slice 8 x 740 ops/sec ±2.92% (76 runs sampled) Slice 64 x 2,301 ops/sec ±2.49% (79 runs sampled) Slice 256 x 3,166 ops/sec ±2.06% (80 runs sampled) Slice 1024 x 3,461 ops/sec ±2.14% (80 runs sampled) Slice 4096 x 3,455 ops/sec ±2.13% (79 runs sampled) Slice 16384 x 3,392 ops/sec ±2.57% (77 runs sampled) Slice 32767 x 3,398 ops/sec ±1.97% (78 runs sampled)

ops/secの値が大きいほど高速です。一個一個処理する場合は、もともと1/10以下になるなどかなり低速です。また、まとめるサイズが大きいほど、速くなっていきますが、1024をこえたあたりからあまり変わらなくなります。安全面を見て、1024,2048,4096あたりにするのが良いのかも知れません。ただ、他のブラウザでは最適なところが異なる場合がありますので、ご注意ください。

投稿2017/09/22 23:16

編集2017/09/22 23:30
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

terater

2019/04/14 10:59

サーバサイドに環境をいじれる状況ではなかったので利用できませんでしたが、ブラウザ側の仕様があることやベンチマークテストの計測方法など理解することが出来ました。。
guest

0

ベストアンサー

この現象には詳しくありませんが、スタックオーバーフローと呼ばれる現象だと思います。
引数の数が多過ぎでスタック領域を使い尽くすケースは見つかりませんでしたが、それが理由であれば、一つずつ繰り返し処理をすれば、回避できると思います。

JavaScript

1var input = this.sampledata.map(String.fromCharCode).join('');

Re: terater さん

投稿2017/09/22 05:03

編集2017/09/22 05:04
think49

総合スコア18164

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

多少内容と用途がちがいますが、こちらで紹介されているような分割が現実的かもしれません

投稿2017/09/22 04:07

yambejp

総合スコア114843

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2017/09/22 04:09

あとはどのような実装をしており、どうやってデータを取得しているかをもう少し詳しく例示されたほうがいいと思います
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問