【问题标题】:Synchronous XMLHttpRequest when reading file in JavaScript在 JavaScript 中读取文件时同步 XMLHttpRequest
【发布时间】:2015-07-19 05:55:15
【问题描述】:

我正在尝试在 js 中实现一个算法。我想从文本文件中读取数据(逐行)无需选择输入,只需在源代码中设置文件路径即可。我已经尝试过stackoverflow上的所有方法,它们都不适合我。

要读取的文件正好在我的本地主机上。

谁能帮忙?

---------更新----------

这是我的代码,出现错误:

主线程上的同步 XMLHttpRequest 已被弃用,因为它会对最终用户的体验产生不利影响。

我要读取的文件正好在我的 localhost 文件夹中。

<html>
    <head>
    </head>
    <body>
        <script type="text/javascript">
            var rules = {};
            var left, right1, right2, probability;
            // read rule file and process it into rules format
            function readTextFile(file) {
                var rawFile = new XMLHttpRequest();
                rawFile.open("GET", file, false);
                rawFile.onreadystatechange = function () {
                    if(rawFile.readyState === 4) {
                        if(rawFile.status === 200 || rawFile.status == 0) {
                            var allText = rawFile.responseText;
                            allText = allText.split("\n");
                            allText = allText.splice(0, allText.length-1);
                            allText.forEach(function (rule) {
                                rule;
                                left = rule[0];
                                probability = parseFloat(rule.slice(rule.indexOf(":")+2));
                                rules[left] ? rules[left] : rules[left] = {}; // init left
                                if (rule[6] == " ") {
                                    right1 = rule[5];
                                    right2 = rule[7];
                                    rules[left][right1] ? rules[left][right1] : rules[left][right1] = {}; // init right1
                                    rules[left][right1][right2] = probability;
                                } else {
                                    right1 = rule.slice(5, rule.indexOf(":") - 1);
                                    rules[left][right1] = probability;
                                }
                            });
                        }
                    }
                }
                rawFile.send(null);
            }
            readTextFile("rules.txt");
            console.log(rules);
        </script>
    </body>
</html>

----------更新2------------- -

规则是这样的:

A -> D C : 0.00688348825155337
A -> D M : 0.03345377673232689
A -> G F : 0.43257400994797
A -> L C : 0.4225829540437112
A -> L H : 0.014080236090023698
A -> L M : 0.06637630954705294
A -> N M : 0.001218212356843953
A -> O M : 0.022583482305501317

【问题讨论】:

  • 你能分享你试过的代码吗?它将帮助我们提供解决方案/建议..
  • 还有什么错误?
  • HTTP GET request in JavaScript? 的可能重复项
  • 如果文件在您的服务器上,您可以使用 AJAX 请求检索整个文件。你必须自己把它分成几行。如果文件在您的本地计算机上,浏览器安全性将阻止您在没有选择输入的情况下读取它。
  • 大家好,我已经更新了我的代码,目前我可以使用这种方法读取文件,但是有一个新描述的警告。你能帮我修一下还是再给我一个独奏?

标签: javascript


【解决方案1】:

您收到的警告是因为通常不鼓励同步 XMLHttpRequest:如果服务器没有立即响应,它可能会挂起浏览器。

如果您只打算在 localhost 上运行它,您可以根据需要忽略警告,或者将同步请求更改为异步请求更好。

使它同步的是.open()调用的第三个参数:

rawFile.open("GET", file, false);

要使其异步,请将其更改为 true:

rawFile.open("GET", file, true);

或者直接省略参数,因为true是默认值:

rawFile.open("GET", file);

您的其余代码应该按原样工作。 (它在快速测试中对我有用。)您已经以一种适用于异步请求的方式编写了代码:当readyState === 4 时,您正在使用onreadystatechange 回调中的数据,而不是假设它在您发出 .open() 调用后将立即可用,同步代码可能会这样做。

请参阅MDN documentation 了解更多信息。

更新 1

rulesonreadystatechange 回调之外未定义的问题是正常的预期行为。请记住,我们现在已将请求更改为异步。也就是说,.open() 调用发起请求,然后立即返回,然后服务器(甚至是localhost 上的服务器)返回数据。

解决方案很简单:在发出请求后不要尝试访问内联数据。相反,只能从成功回调函数访问它,或者从您从该回调调用的另一个函数访问它。

例如,在您更新的代码中,只需将 console.log() 调用移动到成功回调的末尾,如下所示,或者如果您愿意,可以从那里调用您自己的另一个函数。无论您想对数据执行什么其他操作,请遵循相同的模式。

另外,不要在代码顶部声明rules;这只会导致混淆,因为 rules 的值在调用成功回调函数之前不可用(如您所见)。相反,在回调中声明它。

同样,leftright1right2probability 仅在 forEach() 回调中使用,因此应在此处声明它们。

更新 2

正如我们在 cmets 中所讨论的,正则表达式是进行这种字符串解析的好方法。

这是一个示例,说明了原始代码的工作版本和使用正则表达式解析规则的新版本。

我测试的rules.txt 文件如下所示:

A -> D C : 0.00688
A -> G F : 0.43257
B -> with : 0.1875
C -> with : 0.2

JavaScript 代码是:

function readRules( file ) {
    var request = new XMLHttpRequest();
    request.open( 'GET', file );
    request.onreadystatechange = function() {
        if( request.readyState === 4 ) {
            if( request.status === 200 || request.status == 0 ) {
                processRules1( request.responseText );
                processRules2( request.responseText );
            }
        }
    }
    request.send( null );
}

function processRules1( allText ) {
    var rules = {};
    allText = allText.split("\n");
    allText = allText.splice(0, allText.length-1);
    allText.forEach(function (rule) {
        left = rule[0];
        probability = parseFloat(rule.slice(rule.indexOf(":")+2));
        rules[left] ? rules[left] : rules[left] = {}; // init left
        if (rule[6] == " ") {
            right1 = rule[5];
            right2 = rule[7];
            rules[left][right1] ? rules[left][right1] : rules[left][right1] = {}; // init right1
            rules[left][right1][right2] = probability;
        } else {
            right1 = rule.slice(5, rule.indexOf(":") - 1);
            rules[left][right1] = probability;
        }
    });
    console.log( JSON.stringify( rules, null, 4 ) );
}

function processRules2( rulesText ) {
    var reRule = /^(\w) -> (\w+)( (\w))? : ([\d.]+)$/gm;
    var rules = {};
    rulesText.replace( reRule, function(
        line, left, right1, dummy1, right2, probability
    ) {
        if( ! rules[left] ) rules[left] = {};
        if( right2 ) {
            if( ! rules[left][right1] ) rules[left][right1] = {};
            rules[left][right1][right2] = probability;
        } else {
            rules[left][right1] = probability;
        }
    });
    console.log( JSON.stringify( rules, null, 4 ) );
}

readRules( 'rules.txt' );

processRules1() 使用原始技术,processRules2() 使用正则表达式。

两个版本都为测试 rules.txt 文件记录了相同的结果:

{
    "A": {
        "D": {
            "C": 0.00688
        },
        "G": {
            "F": 0.43257
        }
    },
    "B": {
        "with": 0.1875
    },
    "C": {
        "with": 0.2
    }
}

processRules2() 函数中看起来有点吓人的部分是正则表达式本身:

var reRule = /^(\w) -> (\w+)( (\w))? : ([\d.]+)$/gm;

这里不详细解释,我指给你a page on regex101.com,它测试正则表达式并解释它是如何工作的。如果他们的解释中有任何不清楚的地方,请大声告诉我。

除了更短一点之外,正则表达式使代码更加灵活。例如,假设规则文件有时在值之间使用多个空格,而不是现有文件中的单个空格。使用原始方法,您必须重新计算所有字符偏移量并包含处理单个空格和多个空格的代码。使用正则表达式,只需将(匹配一个空格)更改为+(匹配一个或多个空格)。

还要注意,正则表达式负责将规则文件分成几行并忽略文件末尾的空行(而不是使用.splice() 删除最后一行)。空行根本不匹配正则表达式,因此它会被忽略。

代码中一个奇怪的地方是.replace() 方法的使用。通常,当您实际上想用其他内容替换字符串中的字符时,会使用此方法。但是这里我们只是将它用作对字符串中的每个匹配项调用函数的一种方式,然后我们忽略.replace()返回的值。

最后一个建议:在几个地方,原始代码是这样的:

rules[left] ? rules[left] : rules[left] = {};

条件运算符可以方便地用于您实际将结果分配给某物的类似用途,但在这种特殊情况下,使用普通的if 语句更简单、更清晰:

if( ! rules[left] ) rules[left] = {};

【讨论】:

  • 好吧,将 false 更改为 true 以修复我的警告。但是还有一个问题,比如我上面最新的代码中,变量rules在函数之外是没有变化的。但是回调函数中的断点确实表明规则被赋予了价值。你能看看吗?请忽略我的数据结构,只是回调函数内部改变的变量在输出时保持不变。
  • 感谢您的建议,我已经根据您的建议改进了我的代码,现在运行良好。对了,我的规则数据更新了,你说可能有其他的处理方式,你说的是正则表达式吗?非常感谢您的宝贵时间。
  • 太好了,我很高兴你成功了!是的,正则表达式和split 在这里可能会有所帮助,例如您可以将parseFloat(rule.slice(rule.indexOf(":")+2)) 替换为+rule.split(/: */)[1](我假设冒号后跟零个或多个空格)。我将在下面的第二条评论中重复这一点,以避免自动换行......
  • 那是+rule.split(/: */)[1]
  • 是的,我明白了你的想法,目前我对正则表达式不熟悉......我想我今天应该学习一下。感谢您的建议!
猜你喜欢
  • 2013-06-08
  • 1970-01-01
  • 1970-01-01
  • 2011-01-09
  • 2013-10-25
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
相关资源
最近更新 更多