2009-09-03 8 views
0

Je suis en train de faire ce qui suit dans AIR:Parsing fichiers texte volumineux avec Adobe AIR

  1. navigation dans un fichier texte
  2. lire le fichier texte et de le stocker dans une chaîne (en fin de compte dans un tableau)
  3. diviser la chaîne par le delimiter \ n et mettre les chaînes résultantes dans un tableau
  4. manipuler ces données avant de l'envoyer à un site Web (base de données MySQL)

Les fichiers de texte que je traite auront entre 100 et 500mb. Jusqu'à présent, j'ai pu pour compléter les étapes 1 et 2, voici mon code:

<mx:Script> 
    <![CDATA[ 
    import mx.collections.ArrayCollection; 
    import flash.filesystem.*; 
    import flash.events.*; 
    import mx.controls.*; 

    private var fileOpened:File = File.desktopDirectory; 
    private var fileContents:String; 
    private var stream:FileStream; 

    private function selectFile(root:File):void { 
     var filter:FileFilter = new FileFilter("Text", "*.txt"); 
     root.browseForOpen("Open", [filter]); 
     root.addEventListener(Event.SELECT, fileSelected); 
    } 

    private function fileSelected(e:Event):void { 
     var path:String = fileOpened.nativePath; 
     filePath.text = path; 

     stream = new FileStream(); 
     stream.addEventListener(ProgressEvent.PROGRESS, fileProgress); 
     stream.addEventListener(Event.COMPLETE, fileComplete); 
     stream.openAsync(fileOpened, FileMode.READ); 
    } 

    private function fileProgress(p_evt:ProgressEvent):void { 
     fileContents += stream.readMultiByte(stream.bytesAvailable, File.systemCharset); 
     readProgress.text = ((p_evt.bytesLoaded/1048576).toFixed(2)) + "MB out of " + ((p_evt.bytesTotal/1048576).toFixed(2)) + "MB read"; 
    } 

    private function fileComplete(p_evt:Event):void { 
     stream.close(); 
     //fileText.text = fileContents; 
    } 

    private function process(c:String):void { 
     if(!c.length > 0) { 
      Alert.show("File contents empty!", "Error"); 
     } 
     //var array:Array = c.split(/\n/); 

    } 

    ]]> 
</mx:Script> 

Voici le MXML

<mx:Text x="10" y="10" id="filePath" text="Select a file..." width="678" height="22" color="#FFFFFF" fontWeight="bold"/> 
<mx:Button x="10" y="40" label="Browse" click="selectFile(fileOpened)" color="#FFFFFF" fontWeight="bold" fillAlphas="[1.0, 1.0]" fillColors="[#E2E2E2, #484848]"/> 
<mx:Button x="86" y="40" label="Process" click="process(fileContents)" color="#FFFFFF" fontWeight="bold" fillAlphas="[1.0, 1.0]" fillColors="[#E2E2E2, #484848]"/> 
<mx:TextArea x="10" y="70" id="fileText" width="678" height="333" editable="false"/> 
<mx:Label x="10" y="411" id="readProgress" text="" width="678" height="19" color="#FFFFFF"/> 

étape 3 est là où j'ai quelques ennuis. Il y a 2 lignes dans mon code commentées, les deux lignes provoquent le gel du programme.

fileText.text = fileContents; tente de placer le contenu de la chaîne dans une zone de texte
var array: Array = c.split (/ \ n /); tente de diviser la chaîne par délimiteur newline

Pourrait utiliser une entrée à ce stade ... Est-ce que je vais même dans ce sens? Les fichiers flex/air peuvent-ils être aussi grands? (Je suppose donc) Ceci est ma première tentative de faire tout type de travail flex, si vous voyez d'autres choses que j'ai fait mal ou pourrait être mieux fait, j'apprécierais les heads-up!

Merci!

Répondre

4

Faire un split sur un fichier de 500 Mo peut-être pas une bonne idée. Vous pouvez écrire votre propre analyseur pour travailler sur le dossier, mais il peut ne pas être très rapide soit:

private function fileComplete(p_evt:Event):void 
{ 
    var array:Array = []; 

    var char:String; 
    var line:String = ""; 
    while(stream.position < stream.bytesAvailable) 
    { 
     char = stream.readUTFBytes(1); 
     if(char == "\n") 
     { 
      array.push(line); 
      line = ""; 
     } 
     else 
     { 
      line += char; 
     } 
    } 

    // catch the last line if the file isn't terminated by a \n 
    if(line != "") 
    { 
     array.push(line); 
    } 

    stream.close(); 
} 

Je ne l'ai pas testé, mais il devrait tout simplement pas par le caractère de fichier par caractère. Si le caractère est une nouvelle ligne, poussez l'ancienne ligne dans le tableau sinon ajoutez-la à la ligne courante.

Si vous ne voulez pas bloquer votre interface utilisateur pendant que vous le faites, vous aurez besoin de faire abstraction dans une idée à base de minuterie:

// pseudo code 
private function fileComplete(p_evt:Event):void 
{ 
    var array:Array = []; 
    processFileChunk(); 
} 

private function processFileChunk(event:TimerEvent=null):void 
{ 
    var MAX_PER_FRAME:int = 1024; 
    var bytesThisFrame:int = 0; 
    var char:String; 
    var line:String = ""; 
    while( (stream.position < stream.bytesAvailable) 
      && (bytesThisFrame < MAX_PER_FRAME)) 
    { 
     char = stream.readUTFBytes(1); 
     if(char == "\n") 
     { 
      array.push(line); 
      line = ""; 
     } 
     else 
     { 
      line += char; 
     } 
     bytesThisFrame++; 
    } 

    // if we aren't done 
    if(stream.position < stream.bytesAvailable) 
    { 
     // declare this in the class 
     timer = new Timer(100, 1); 
     timer.addEventListener(TimerEvent.TIMER_COMPLETE, processFileChunk); 
     timer.start(); 
    } 
    // we're done 
    else 
    { 
     // catch the last line if the file isn't terminated by a \n 
     if(line != "") 
     { 
      array.push(line); 
     } 

     stream.close(); 

     // maybe dispatchEvent(new Event(Event.COMPLETE)); here 
     // or call an internal function to deal with the complete array 
    } 
} 

Fondamentalement, vous choisissez une quantité du fichier à traiter chaque image (MAX_PER_FRAME), puis traiter autant d'octets. Si vous dépassez le nombre d'octets, faites simplement une minuterie pour rappeler la fonction process dans quelques instants et elle devrait continuer là où elle s'est arrêtée. Vous pouvez envoyer un événement d'appel d'une autre fonction une fois que vous êtes sûr que vous êtes complet.

+0

vous remercie beaucoup pour la perspicacité. Par curiosité, je me suis toujours demandé comment les autres apprenaient flex/as3. cela vous dérangerait-il de partager un peu de votre expérience? – marauder

+0

je suis parti de flex en une semaine http://www.adobe.com/devnet/flex/videotraining/ puis flex 3 en action http://www.amazon.com/Flex-3-Action-Tariq-Ahmed/ dp/1933988746 puis Flex Exemples http://blog.flexexamples.com/ –

1

Essayez de le traiter en pièces.

2

Je suis d'accord. Essayez de diviser le texte en morceaux pendant que vous le lisez dans le flux.

De cette façon, vous ne devez pas stocker le texte dans votre chaîne fileContents (réduire l'utilisation de la mémoire de 50%)

0

flux.position < flux.bytesAvailable Cette condition ne serait-elle pas fausse après que la position atteigne le milieu du fichier? Si le fichier est de 10 octets, après avoir lu 5 octets alors bytesAvailable sera 5, j'ai stocké la valeur initiale dans une autre variable et l'ai utilisée dans la condition. En plus de cela, je pense que c'est plutôt bon

1

En ce qui concerne l'analyseur de James, il y a un problème si les fichiers texte contiennent des caractères UTF multi-octets (j'essayais d'analyser les fichiers UTF de la même manière quand je suis tombé dessus ce fil). Convertir chaque octet en une chaîne individuelle va désintégrer les caractères multi-octets, donc j'ai fait quelques modifications.

Afin de rendre cet analyseur multi-octets convivial, vous pouvez stocker les lignes en croissance dans un ByteArray plutôt que d'une chaîne. Puis, quand vous frappez la fin d'une ligne (ou un morceau, ou le fichier), vous pouvez l'analyser comme une chaîne UTF (si nécessaire) sans aucun problème:

var 
    out :ByteArray, 
    line_out :String, 
    line_end :Number, 
    char :int, 
    line:ByteArray; 

out = new ByteArray(); 
line = new ByteArray(); 

while(file_stream.bytesAvailable > 0) 
{ 
    char = file_stream.readByte(); 
    if((String.fromCharCode(char) == "\n")) 
    { 
     // Do some processing on a line-by-line basis 
     line_out = ProcessLine(line); 
     line_out += "\n"; 
     out.writeUTFBytes(line_out); 
     line = new ByteArray(); 
    } 
    else 
    { 
     line.writeByte(char); 
    } 
} 
//Get the last line in there 
out.writeBytes(line);