为 emscripten HTML 程序提供标准输入?

Posted

技术标签:

【中文标题】为 emscripten HTML 程序提供标准输入?【英文标题】:Providing stdin to an emscripten HTML program? 【发布时间】:2015-10-02 16:55:05 【问题描述】:

我有一个 C 程序,它通过命令行获取一个参数(一个字符数组/字符串),并且还从标准输入中读取。我已经使用 emscripten 将其编译为 javascript。这很成功,我可以像使用 node.js 的普通 C 程序一样运行它:

emcc -O2 translate.c
node translate.js "foo" < bar.txt

如您所见,我将字符串“foo”作为参数提供,并将 bar.txt 的内容作为标准输入。现在我希望这是一个独立的 html 文件。

通过将输出更改为 HTML:

emcc -O2 translate.c -o trans.html

我将adding arguments: ['foo'], 的参数提供给var Module 中的定义。这按预期工作,程序正确接收参数。

现在,我如何为这个程序提供标准输入输入?我不需要动态地执行此操作。只需在 HTML 中的某处声明一个带有所需标准输入内容的字符串就可以了。

【问题讨论】:

对 emscripten 不太熟悉,但看起来 --embed-file 可能符合要求。 @nrabinowitz 刚刚找到了解决方案。谢谢! @minxomat 您应该将解决方案添加为答案(并将其视为正确),而不是作为问题的编辑 @AlvaroMontoro 我确信这实际上不是“正确”的解决方案。总的来说,我对 JS 或 emscripten 不够精通,无法判断这种方法的质量。 @mınxomaτ 更有理由将其添加为答案,因此其他人可以与所有其他答案一起对其进行投票...我将删除它,但欢迎您如果需要,请重新发布它作为答案。 【参考方案1】:

一种方法是使用 Emscripten 文件系统 API,例如在 Module preRun 函数中调用 FS.init,传递自定义函数以用于标准输入、输出和错误。

var Module = 
  preRun: function() 
    function stdin() 
      // Return ASCII code of character, or null if no input
    

    function stdout(asciiCode) 
      // Do something with the asciiCode
    

    function stderr(asciiCode) 
      // Do something with the asciiCode
    

    FS.init(stdin, stdout, stderr);
  
;

这些函数是相当低级的:它们每次处理一个字符作为 ASCII 码。如果您有要传入的字符串,则必须自己迭代字符串的字符。我怀疑charCodeAt 会有所帮助。要从 stdout 或 stderr 输出字符串,我怀疑 fromCharCode 会有所帮助。

使用每个示例(没有经过很好的测试!)实现如下。

var input = "This is from the standard input\n";
var i = 0;
var Module = 
  preRun: function() 
    function stdin() 
      if (i < res.length) 
        var code = input.charCodeAt(i);
        ++i;
        return code;
       else 
        return null;
      
    

    var stdoutBuffer = "";
    function stdout(code) 
      if (code === "\n".charCodeAt(0) && stdoutBuffer !== "") 
        console.log(stdoutBuffer);
        stdoutBuffer = "";
       else 
        stdoutBuffer += String.fromCharCode(code);
      
    

    var stderrBuffer = "";
    function stderr(code) 
      if (code === "\n".charCodeAt(0) && stderrBuffer !== "") 
        console.log(stderrBuffer);
        stderrBuffer = "";
       else 
        stderrBuffer += String.fromCharCode(code);
      
    

    FS.init(stdin, stdout, stderr);
  
;

【讨论】:

太棒了!我无法摆脱ReferenceError: FS is not defined - github.com/kripken/emscripten/issues/… 任何想法? 我确认这适用于 emscripten 本身提供的 test_stdin.c 程序。谢谢!【参考方案2】:

你可以修改 Window 对象,而不是编辑 Emscripten 的输出

window.prompt = function() 
  return 'This will appear to come from standard input';
;

不太好,但我认为这与其说是编辑 Emscripten 生成的 Javascript 不如说是一种 hack。

【讨论】:

抱歉,没有理由阻止这个默认功能。与此相比,编辑输入处理程序是更安全、更简单的方法。这样我就可以使用stdInput = read('input'); 来传递整个输入。查看here(第 86 行+)我如何调用 emscripten 脚本。 :) @minxonat 我认为这是有原因的:让构建不那么容易受到 Emscripten 生成的输出变化的影响。我怀疑大多数项目不使用 window.prompt,但确实需要处理对其依赖项的更新。这是一个权衡:您失去了按设计使用 window.prompt 的能力,但获得了代码,因为更新 Emscripten / 其配置可能会破坏其输出的自动修补,因此更有可能表现得更好。注意:“有更好的机会”是基于我自己的判断,即修补生成的代码很脆弱:我无法提供任何证据来支持这一点。 我确实相信从标准 JS API 修补函数会更加脆弱。 您的脚本可以将window.prompt 保存在另一个变量中,然后调用它作为您的提示,并让原始的工作用于 wasm 而无需修改它。我同意 Michal 的观点,monkeypatching 比修改 wasm.js 更好,但是可以实现 window.readline 或自定义函数,然后在 wasm.js 中删除或重新排序 wasm 的 prompt 条件分支。如果readline首先在get_char中进行了测试,那么很容易推荐。【参考方案3】:

根据问题“编辑”,我做了我的功能,谢谢。

只是希望下面的代码可以帮助别人。

    注释运行();在 emscript 结尾

    // in my emscript 
    
    // shouldRunNow refers to calling main(), not run().
    var shouldRunNow = true;
    if (Module['noInitialRun']) 
        shouldRunNow = false;
    
    //run(); // << here
    // POST_RUN_ADDITIONS
    

    result = areaInput(); // 如题所述

    在您的 html 文件中添加以下代码以激活 emscript 中的 run()

    <script>
    var message;
    var point = -1;
    function getArea()
        message = document.getElementById('input').value.split('\n');
    
    function areaInput()
        if(point >= message.length - 1)
            return null;
        
        point += 1;
        return message[point];
    
    function execEmscript()
        window.console = 
            log: function(str)
                document.getElementById("output").value += "\n" + str;
            
        
        getArea();
        run();
    
    </script>
    

    记住 html 中的 io textareas

    &lt;textarea id="input" cols="80" rows="30"&gt;&lt;/textarea&gt;

    &lt;textarea id="output" cols="80" rows="30"&gt;&lt;/textarea&gt;

    还有一个按钮

    &lt;button onclick="execEmscript();"&gt;run&lt;/button&gt;

【讨论】:

以上是关于为 emscripten HTML 程序提供标准输入?的主要内容,如果未能解决你的问题,请参考以下文章

Emscripten教程之Emscripten的运行时环境

Emscripten:提供下载/保存生成的 MEMFS 文件

Emscripten canvas + jQuery - 切换焦点

从 EMSCRIPTEN 访问 DOM

将命令行参数传递给 emscripten 生成的应用程序

Emscripten:用控制台(某种)替换提示