2016-08-02 31 views
5

Mam zaprojektował ten regex z kilku narzędzi internetowych oraz z pomocą społeczności:Prawidłowe RegEx według internetowych narzędzi testowych, nie otrzymuję żadnych mecze podczas odczytu pliku w przeglądarce

https://regex101.com/r/hJ4pD5/1

(\s[A-Z]\.).+?(?=(\s[A-Z]\.)|(\W?(Answer:)\W?)) 

The celem jest wyciągnięcie wszystkich alternatyw dla pytania. Według regexr i regex101 jest to prawidłowy regex JavaScript, która działa dobrze z test data(pastebin):

1. Question goes here: 
A. Answer one 
B. Answer two 
C. Answer three D. Not indented Answer 
Answer: B is correct 

Oczekiwane wyniki powinny być:

"jeden A. Answer", "B. odpowiedzieć na dwa", "C. Odpowiedź trzecia", "D. Nie wcięta odpowiedź"

Ale kiedy zaimplementuję to w kodzie, to nie działa zbyt dobrze, nie znaleziono pasujących wyników.

(Spróbuj go z danymi Pastebin)

/** 
* Created by Schwusch on 01/08/2016. 
*/ 
$(document).ready(start); 
var questionsRaw; 
var questionsFormatted = []; 
var questionIndex = 0; 

function readSingleFile(e) { 
    var file = e.target.files[0]; 
    if (!file) { 
     return; 
    } 
    var reader = new FileReader(); 
    reader.onload = function(e) { 
     var contents = e.target.result; 
     displayContents(contents); 
    }; 
    reader.readAsText(file); 
} 
/* REGEX MAGIC -------------------------------------------------*/ 
function displayContents(contents) { 
    questionsRaw = contents.split('---'); 
    $.each(questionsRaw, function(index, question) { 
     var answer = question.split("Answer:")[1]; 
     var splittedQuestion = question.split("A.")[0]; 
     var alternatives = question.match(/(\s[A-Z]\.).+?(?=(\s[A-Z]\.)|(\W?(Answer:)\W?))/g); 
     questionsFormatted.push({ 
      question: splittedQuestion, 
      alternatives: alternatives, 
      answer: answer 
     }); 
    }); 
/* END REGEX MAGIC -------------------------------------------------*/ 
    var element = document.getElementById('file-content'); 
    element.innerHTML = questionsFormatted[questionIndex].question; 
    for (var i = 0; i < questionsFormatted[questionIndex].alternatives.length ; i++) { 
     $('#alternatives').append('<button type="button" class="list-group-item">' + questionsFormatted[questionIndex].alternatives[i] + '</button>'); 
    } 
} 
function start() { 
    document.getElementById('file-input') 
     .addEventListener('change', readSingleFile, false); 

    $(window).keydown(function(e) { 
     e = e || event; 
     switch(e.keyCode) { 
      case 37: // left 
       previousQuestion(); 
       return false; 
      case 38: // up 
       showQuestion(); 
       return false; 
      case 39: // right 
       nextQuestion(); 
       return false; 
      case 40: // down 
       showAnswer(); 
       return false; 
     } 
    }); 
    $(document).on('change', ':file', function() { 
     var input = $(this), 
      numFiles = input.get(0).files ? input.get(0).files.length : 1, 
      label = input.val().replace(/\\/g, '/').replace(/.*\//, ''); 
     input.trigger('fileselect', [numFiles, label]); 
    }); 

    $(':file').on('fileselect', function(event, numFiles, label) { 
     var element = document.getElementById('filechoose'); 
     element.innerHTML = label; 
    }); 
} 

function showAnswer() { 
    var element = document.getElementById('file-content'); 
    element.innerHTML = questionsFormatted[questionIndex].answer; 
} 

function showQuestion() { 
    var element = document.getElementById('file-content'); 
    element.innerHTML = questionsFormatted[questionIndex].question; 
} 

function nextQuestion() { 
    if (questionIndex < questionsFormatted.length - 1) questionIndex++ ; 
    else questionIndex = 0; 
    var element = document.getElementById('file-content'); 
    element.innerHTML = questionsFormatted[questionIndex].question; 
    $(".list-group-item").remove(); 
    for (var i = 0; i < questionsFormatted[questionIndex].alternatives.length ; i++) { 
     $('#alternatives').append('<button type="button" class="list-group-item">' + questionsFormatted[questionIndex].alternatives[i] + '</button>'); 
    } 
} 

function previousQuestion() { 
    if (questionIndex > 0) questionIndex-- ; 
    else questionIndex = questionsFormatted.length - 1; 
    var element = document.getElementById('file-content'); 
    element.innerHTML = questionsFormatted[questionIndex].question; 
    $(".list-group-item").remove(); 
    for (var i = 0; i < questionsFormatted[questionIndex].alternatives.length ; i++) { 
     $('#alternatives').append('<button type="button" class="list-group-item">' + questionsFormatted[questionIndex].alternatives[i] + '</button>'); 
    } 
} 
<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <title>Question tool</title> 
    <script src="https://code.jquery.com/jquery-3.1.0.js" 
      integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"></script> 

    <!-- Latest compiled and minified CSS --> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" 
      integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> 

    <!-- Optional theme --> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" 
      integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> 
    <script src="script.js"></script> 
    <style> 
     /* Move down content */ 
     body { 
      padding-top: 20px; 
      padding-bottom: 20px; 
     } 

    </style> 
</head> 
<body> 

<div> 
    <div class="container"> 

     <div class="jumbotron"> 
      <h3>Question Tool</h3> 
      <label class="btn btn-default btn-file" id="filechoose"> 
       Choose File 
       <input type="file" id="file-input" style="display: none;"/> 
      </label> 
      <div class="btn-group btn-group-justified" role="group" aria-label="..."> 
       <div class="btn-group" role="group"> 
        <button type="button" class="btn btn-lg btn-primary" onclick="showAnswer()" role="button"> 
         <span class="glyphicon glyphicon-arrow-down" aria-hidden="true"></span>Show Answer 
        </button> 
       </div> 
       <div class="btn-group" role="group"> 
        <button type="button" class="btn btn-lg btn-success" onclick="showQuestion()" role="button"> 
         <span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>Show Question 
        </button> 
       </div> 
       <div class="btn-group" role="group"> 
        <button type="button" class="btn btn-lg btn-danger" onclick="previousQuestion()" role="button"> 
         <span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span>Previous Question 
        </button> 
       </div> 
       <div class="btn-group" role="group"> 
        <button type="button" class="btn btn-lg btn-info" onclick="nextQuestion()" role="button"> 
         <span class="glyphicon glyphicon-arrow-right" aria-hidden="true"></span>Next Question 
        </button> 
       </div> 
      </div> 

      <div id="file-content" class="well"></div> 
      <div id="alternatives" class="list-group"> 
      </div> 
     </div> 

    </div> 
</div> 

</body> 
</html> 

Dlaczego to działa w testerów online, ale nie w przeglądarce?

+0

Nie mogę uruchomić kodu usterki z powodu innych problemów, ale wyrażenie regularne działa poprawnie na konsoli przeglądarki (aczkolwiek przy każdym dopasowaniu musi być przycięty odstęp) – moopet

+0

Tytuł nie jest poprawnie sformułowany, regex jest poprawny ale nie dostał żadnych meczów. Dziękuję ci za wysiłek. – Schwusch

Odpowiedz

5

Powodem, dla którego nie działa on dla Ciebie, jest to, że w przeciwieństwie do tekstu używanego podczas testów na regex101.com, ładowany plik używa \r\n jako sekwencję nowej linii, zamiast tylko \n.

Dodajmy do tego, że domyślnie meta znak . nie pasuje \r i że JavaScript nie obsługuje modyfikator s które mogłyby zmienić to zachowanie, można uzyskać mniej lub w ogóle żadnego meczu.

Bardziej konkretnie: w wyrażeniu regularnym część .+? przestanie pasować do znaków po napotkaniu \r. Najpierw robi to, ponieważ patrzy w przyszłość i znajduje dopasowanie do \r z \s lub \W, ale następny \n nie pasuje ani do [A-Z] ani do A z Answer:. Dlatego też kontynuuje działanie i próbuje kontynuować z częścią .+?, ale to również się nie udaje, ponieważ \r nie może się równać z tym, jak wyjaśniono powyżej. Dlatego proces dopasowywania rozpoczyna się ponownie na początku wyrażenia regularnego, aby znaleźć potencjalny następny mecz. A to się nie udaje z tych samych powodów.

Aby rozwiązać ten problem, należy zmienić dwie rzeczy:

  • dodać + po \s w środku, więc nie tylko dopasować \n, ale również poprzedzające \r.

  • Zmień \W? na \W*, więc znowu może pasować do poprzedniego \r.

To powinno działać:

/(\s[A-Z]\.).+?(?=(\s+[A-Z]\.)|(\W*(Answer:)\W?))/g 

Mimo to rozwiązuje go, chciałbym również zasugerować, aby uprościć ten regex dalej:

/\s[A-Z]\..+?(?=\s+[A-Z]\.|\W*Answer:)/g 

zwłaszcza \W? na końcu ma sensu : albo pasuje do \W, albo nie, w obu przypadkach to akceptujesz.

+0

To bardzo konstruktywne i pouczające, dziękuję. Czy "\ r \ n" jest zwyczajowo używane w plikach tekstowych, ale nie w polach takich jak obszary tekstowe? Czy jest jakiś inny powód, dla którego jest inny? – Schwusch

+1

Standardowa sekwencja nowego wiersza (https://en.wikipedia.org/wiki/Newline#Representations) w plikach zależy od systemu operacyjnego, na którym się znajdujesz: Windows używa '\ r \ n', systemy oparte głównie na Uniksie' \ n'. W przeglądarkach DOM zwróci głównie zawartość z '\ n' znakami nowej linii, niezależnie od systemu operacyjnego, na którym działają. Powodem różnic jest [historyczny] (https://en.wikipedia.org/wiki/Newline#Representations#History). – trincot