0

I am trying to implement an algorithm in js. I want to read the data from a text file(line by line) without selection input, simply by set the file path in the source code. I have tried every method on stackoverflow, they all don't work for me.

The file to be read is exactly on my localhost.

Can anyone help?

---------update----------

here is my code and there's an error occur as:

Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience.

The file i am trying to read is exactly in my localhost folder.

<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>

---------------------Update2--------------------------

Rules are like this:

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
Summer Sun
  • 947
  • 13
  • 33
  • 1
    Can you share the code you tried? It will help us in providing a solution / suggestion.. – JGV Jul 19 '15 at 06:00
  • Also what errors you were getting? – Obscure Geek Jul 19 '15 at 06:03
  • possible duplicate of [HTTP GET request in JavaScript?](http://stackoverflow.com/questions/247483/http-get-request-in-javascript) – Jonathan Jul 19 '15 at 06:06
  • If the file is on your server you can retrieve the entire file with an AJAX request. You'll have to split it into lines yourself. If the file is on your local computer, browser security will prevent you from reading it without a selection input. –  Jul 19 '15 at 06:12
  • hello all, I have update my code, currently i can read the file use this method but there's a warning as newly described. Could you help me fix it or give me another solotion? – Summer Sun Jul 19 '15 at 06:45
  • Your error can be fixed by using an asynchronous call (which you should be doing anyway). You may need to modify any surrounding code to work that way. –  Jul 19 '15 at 07:01

1 Answers1

0

The warning you are getting is because synchronous XMLHttpRequest is generally discouraged: it can hang up the browser if the server does not respond immediately.

If you're only going to run this on localhost you could disregard the warning if you like, or better, change the synchronous request to asynchronous.

What's making it synchronous is the third parameter to the .open() call:

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

To make it asynchronous, change that to true:

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

Or simply omit the parameter because true is the default:

rawFile.open("GET", file);

The rest of your code should work as is. (It worked for me in a quick test.) You've already written the code in a way that will work with an asynchronous request: you're using the data inside the onreadystatechange callback when readyState === 4, instead of assuming it will be available immediately after you issue the .open() call as synchronous code might do.

See the MDN documentation for more information.

Update 1

The problem with rules being undefined outside the onreadystatechange callback is normal, expected behavior. Remember that we've now changed the request to be asynchronous. That is, the .open() call initiates the request and then returns immediately, before the data is returned by the server (even a server on localhost).

The solution to this is simple: do not attempt to access the data inline after making the request. Instead, access it only from within the success callback function, or from another function you call from that callback.

For example, in your updated code, simply move the console.log() call to the end of the success callback as shown below, or call another function of your own from there if you wish. Whatever else you want to do with the data, follow this same pattern.

Also, don't declare rules at the top of the code; this will only lead to confusion because the value of rules is not available until the success callback function has been called (as you discovered). Instead, declare it inside the callback.

Similarly, left, right1, right2, and probability are only used inside the forEach() callback, so that is where they should be declared.

Update 2

As we discussed in the comments, regular expressions can be a great way to do this kind of string parsing.

Here is an example that illustrates both a working version of the original code, and a new version that uses regular expressions to parse the rules.

The rules.txt file I tested with looks like this:

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

The JavaScript code is:

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() uses the original technique, and processRules2() uses a regular expression.

Both versions log the same result for the test rules.txt file:

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

The one part of the processRules2() function that may seem a bit intimidating is the regular expression itself:

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

Rather than explain it here in detail, let me point you to a page on regex101.com that tests the regular expression and explains how it works. Give me a shout if anything in their explanation isn't clear.

Besides being a bit shorter, the regular expression makes the code much more flexible. For example, suppose the rules file sometimes used more than one space between the values instead of the single space in your existing file. With the original approach, you'd have to recalculate all the character offsets and include code to handle both the single space and multiple spaces. With the regular expression, it would be as simple as changing a (which matches a single space) to + (which matches one or more spaces).

Also note that the regular expression takes care of splitting the rules file into lines and ignoring the empty line at the end of the file (instead of using the .splice() to remove the last line). The empty line simply doesn't match the regular expression so it gets ignored that way.

One odd thing in the code is the use of the .replace() method. Normally this method is used when you actually want to replace characters in a string with something else. But here we are just using it as a way to call a function on each match in the string, and then we ignore the value returned by .replace().

And one last suggestion: In a couple of places the original code is something like this:

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

The conditional operator can be handy for similar kinds of uses where you're actually assigning the result to something, but in this particular case it is simpler and more clear to use an ordinary if statement:

if( ! rules[left] ) rules[left] = {};
Michael Geary
  • 28,450
  • 9
  • 65
  • 75
  • well change the false to true do fix my warning. But there's another problem, for example, in my latest code above, the variable rules is unchanged outside the function. But the breakpoints in the call back function do show that the rules is given value. Could you have a look? please ignore my data structure, simply the variable which is changed inside the call back function remain unchanged when outputted. – Summer Sun Jul 19 '15 at 08:09
  • Thank you for your suggestion, I have improve my code with your advice and it works well now. By the way, my rules data is updated, you said there may be some other way to process it, are you meaning regular expressions? Really thanks for your time. – Summer Sun Jul 20 '15 at 08:48
  • Great, I'm glad you got it working! Yes, regular expressions and `split` could help a little here, for example you could replace `parseFloat(rule.slice(rule.indexOf(":")+2))` with `+rule.split(/: */)[1]` (I'm assuming the colon is followed by zero or more spaces). I'll repeat that in a second comment below to avoid word wrap... – Michael Geary Jul 20 '15 at 09:05
  • That was `+rule.split(/: */)[1]` – Michael Geary Jul 20 '15 at 09:06
  • Yes i have got your idea, currently i am not familiar with regular expression... i think i should study it today. Thanks for your suggestion! – Summer Sun Jul 20 '15 at 09:09
  • I think I have an even better idea, but first one question: You have a test `if (rule[6] == " ")` - but in all the rules I see, `rule[6]` is indeed a space. Do you have an example rule where that character is nonblank? – Michael Geary Jul 20 '15 at 09:16
  • yes there could be a rule like below: B -> with : 0.18750000000000008 – Summer Sun Jul 20 '15 at 11:24
  • sorry for the delay i was out and having dinner... these rules are used for nlp parsing – Summer Sun Jul 20 '15 at 11:25
  • No worries, I've been tied up the last couple of days myself, but finally added a regular expression version of the code to the answer - have a look at it and give me a shout with any questions. – Michael Geary Jul 23 '15 at 00:12