Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Reading and writing to a PARAM.CMI file?

New Here ,
Apr 04, 2013 Apr 04, 2013

Hello,

I have a problem with reading from and writing to a PARAM.CMI file. If I remove the "Section" block at the top and move the first element to the top I can read it fine but I just can't get past the sections.

Section ex: "[Core]" If I delete that and move everything to the top no problemo. Any help would be appreciated.

TOPICS
ActionScript
1.9K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Deleted User
Apr 05, 2013 Apr 05, 2013

Yep. Oops. That was Air code. You'd use URLLoader and URLRequest instead of FileStream and File. My bad. So if you've done the URLRequest thing and got your file into a URLLoader the first function I gave you becomes:

private function loadCMIFile(evt:Event):void {

     var cmiLoader:URLLoader = evt.currentTarget as URLLoader;

     try {

          parseCMI(cmiLoader.data);

     } catch (err:Error) {

          trace(err.name,err.message + "\rUnable to parse selected file.");

     }

     var lessonStatus:S

...
Translate
Guest
Apr 04, 2013 Apr 04, 2013

Do you have the specifications for the cmi file? Do you read it as a bytearray?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Apr 04, 2013 Apr 04, 2013

The file looks like this:

[Core]

Lesson_Status=P

Time=00:18:34

Lesson_Location=66107

Score=20

[ObjectivesStatus]

J_ID.0=02015090020

J_Status.0=F

J_Score=20

I really just have to get to the values of each element by name as plain text.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Apr 04, 2013 Apr 04, 2013

So it is just a text file and you want to parse it to access the values? I apologize I suppose I shouldn't be answering unless I know exactly what a param.cmi file is.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Apr 05, 2013 Apr 05, 2013

It's quite alright since I really don't know much about it either. I've attached a link to that shows some info but it isn't visible here so hopefully it attached. Either way, yes it does look like a plain text file and I was able to parse some of it but getting past the group/sections has been a challenge.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Apr 05, 2013 Apr 05, 2013
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Apr 05, 2013 Apr 05, 2013

There's an infinite amount of ways to read a text file. Here's how I do it.

First you import the file into a string and send it to a parse function. This code has been called from a File.browseForOpen event when the text file is selected.

private function loadCMIFile(evt:Event):void {

     var cmiFile:File = evt.target as File;

     var cmi:Object = {};

     var fileStream = new FileStream();

     fileStream.open(cmiFile, FileMode.READ);

     try {

          cmi = parseCMI(fileStream.readUTFBytes(fileStream.bytesAvailable)));

     } catch (err:Error) {

          trace(err.name,err.message + "\rUnable to parse selected file.");

     }

     //Then, as an example of how to get at your stuff:

     var lessonStatus:String = "Lesson_Status";

    trace(cmi[lessonStatus]);

}

I make a crop function that simplifies how I access things in the string:

private function crop(str:String, start:RegExp, end:RegExp, off1:int=0, off2:int=0):String {

   return str.substring(str.search(start)+off1,str.search(end)+off2)

}//crops a string using two regular expressions to identify stop and start, off1 and off2 allow you to offset some number of characters from the start and end positions, respectively

Then you parse everything you want into an Object so it's easy to get at:

private function parseCMI(str:String):Object {

      var cmi:Object = {};

     cmi["Lesson_Status"] = crop(str, /Lesson_Status=/, /\s+Time=/);

     cmi["Time"] = crop(str, /Time=/, /\s+Lesson_Location/);

     cmi["Lesson_Location"] = crop(str, /Lesson_Location=/, /\s+Score=/);

     //and so  on..

     //

     //

     //

     cmi["J_Score"] = str.substr(str.search(/J_Score=/));//last one!

}

Done! Hopefully this gets you to where you want to be. Of course this only works if everything stays in the same order in the file. If it does not, then you will have to design your crop function differently.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Apr 05, 2013 Apr 05, 2013

Just a note: If the file contains ASCII, the parser should read past the [Core], [ObjectiveStatus], etc sections without a problem. However it is possible that those sections are some sort of binary header that might contain useful information that could be used to build a faster parser. The link doesn't describe them in much detail. I also noticed that there are many more keywords in the file than what you specified above. If you can't use my method because the order is variable or you need to accomodate more keywords...you will need a more complicated search that finds the keyword string, parses that out, uses it as a key in your object, then parses the result.

Before I'd give you code for that, I'd really need to know what the end product is...maybe you need to parse this into XML instead of an object...or keep track of which keywords were parsed.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Apr 05, 2013 Apr 05, 2013

First off thanks for all of your help! You are correct, the file will contain more than what I submitted; I was just giving an example of what it would look like. Let me tell you what I'm trying to accomplish. I'm converting some courseware from Authorware to Flash. The courseware runs with a very old TMS(Training Management System) and uses .cmi files to transfer student data (I assume anyway). So, as a start I wanted to see how I could duplicate the files from Flash. Btw, is the code that you just wrote up for AIR? I was under the assumption that FileStream was an AIR class.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Apr 05, 2013 Apr 05, 2013

Yep. Oops. That was Air code. You'd use URLLoader and URLRequest instead of FileStream and File. My bad. So if you've done the URLRequest thing and got your file into a URLLoader the first function I gave you becomes:

private function loadCMIFile(evt:Event):void {

     var cmiLoader:URLLoader = evt.currentTarget as URLLoader;

     try {

          parseCMI(cmiLoader.data);

     } catch (err:Error) {

          trace(err.name,err.message + "\rUnable to parse selected file.");

     }

     var lessonStatus:String = "Lesson_Status";

     trace(cmi[lessonStatus]);

}

Just parsing the text we can make an object or XML in flash that contains the data in a readable fashion, but if you want to reconstruct the cmi file from Flash then you will need to know what those headers contain. I mean if there is no information and they aren't text you will need to know how many bytes they are and then put them aside in a ByteArray so that you can tack them onto your output file later. Or you will need to find the file specifications for them - they could be full of byte location tags or ids or who knows.

Assuming all we have to worry about is parsing, I do have a suggestion. Maybe you want your data to be an XML in Flash:

private var cmiXML:XML =

<CMI>

<Core>

     <Student_ID/>

     <Student_Name/>

     <Output_File/>

     ...

</Core>

<Core_Vendor>

     <CoursePath/>

</Core_Vendor>

<Evaluation>

     <Course_ID/>

     <Comments_File/>

     <Objectives_Status_File/>

</Evaluation>

...

</CMI>

To Save time searching for keywords, you can set up a set of vectors that contain them

private const cmiCore:Vector.<String> = new <String>["Student_ID", "Student_Name", "Output_File"...etc.];

private const cmiCoreVendor:Vector.<String> = new <String>["CoursePath"];

private const cmiEvaluation:Vector.<String> = new <String>["Course_ID","Comments_File", "Objectives_Status_File"];

etc...

Then you can parse like this:

private function parseCMI(string:String):void {

     //parse Core

     for (var i:int = 0; i < cmiCore.length; i++ {

          var regExp:RegExp = new RegExp(cmiCore+"=");

          if (regExp.test(string) {

               cmiXML.core.child(cmiCore) = extract(string, regExp);

          }

     ]

}

private function extract(str:String, re:RegExp):String {

     var value:String = str.substr(str.search(re));                    //makes a string that starts after the equals sign

     value = value.substring(0,value.search(/[\r\n]/);           //cuts off the string at the first newline or carriage return

     return value;

}

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Apr 05, 2013 Apr 05, 2013

Thanks for all of your time!!! I made the modifications and wrote it out like this just for testing and it has me on the right track. Now I think I can continue on from here.

import flash.net.URLLoader;

import flash.net.URLRequest;

import flash.events.Event;

import flash.net.URLVariables;

stage.addEventListener(MouseEvent.CLICK, loadCMIFile);

function loadCMIFile(evt:MouseEvent):void {

     var cmiRequest:URLRequest = new URLRequest("PARAM.CMI") ;

     var cmiLoader:URLLoader = new URLLoader(cmiRequest);

     cmiLoader.load(cmiRequest);

     cmiLoader.addEventListener(Event.COMPLETE, test);

}

function test(evt:Event):void {

      var cmi:Object = {};

try {

     cmi = parseCMI(evt.target.data);

}

catch (err:Error) {

      trace(err.name,err.message + "\rUnable to parse selected file.");

}

     //Then, as an example of how to get at your stuff:

      var lessonStatus:String = "Lesson_Location";

     trace(cmi[lessonStatus]);

}

function crop(str:String, start:RegExp, end:RegExp, off1:int=0, off2:int=0):String {

     return str.substring(str.search(start)+off1,str.search(end)+off2);

}

//crops a string using two regular expressions to identify stop and start, off1 and off2 allow you to offset some number of characters from the start and end positions, respectively

function parseCMI(str:String):Object {

     var cmi:Object = {};

     cmi["Lesson_Status"] = crop(str,/Lesson_Status=/,/\s+Time=/);

     cmi["Time"] = crop(str,/Time=/,/\s+Lesson_Location/);

     cmi["Lesson_Location"] = crop(str,/Lesson_Location=/,/\s+Score=/);

     return cmi;

}

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Apr 05, 2013 Apr 05, 2013

Right. Well we know the crop function won't work since we can't assume the order or the number of keywords...so you can use the new code and new extract function I posted above instead.

As fas as working with XML or objects, it's your choice. I prefer objects...I just gave XML as an alternative. If you'd rather work with objects, replace the parseCMI function in my last post with this:

private function parseCMI(string:String):Object {

     //parse Core

     var obj:Object = {}

     for (var i:int = 0; i < cmiCore.length; i++) {

          var regExp:RegExp = new RegExp(cmiCore+"=");

          if (regExp.test(string) {

               obj[cmiCore] = extract(string, regExp);

          }

     }

     return obj;

}

This will allow you to create an object without knowing how many keywords there are. Or where they are.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Apr 05, 2013 Apr 05, 2013

I decided to go obsessive compulsive and send you a modified version of your code. You are certainly on the right track...

import flash.net.URLLoader;

import flash.net.URLRequest;

import flash.events.Event;

import flash.net.URLVariables;

private const cmiCore:Vector.<String> = new <String>["Student_ID", "Student_Name", "Output_File","Credit","Lesson_Location","Lesson_Status","Path","Score","Time","Student_First_Name","Student_Last_Name"];

private const cmiVendor:Vector.<String> = new <String>["CoursePath"];

private const cmiEvaluation:Vector.<String> = new <String>["Course_ID","Comments_File", "Objectives_Status_File"];

private const cmiObjectives:Vector.<String> = new <String>["J_ID.1","J_Score.1","J_Status.1"];

private const cmiDemographics:Vector.<String> = new <String>["Command_Name","Hull_Number","Crew","Division","Paygrade","Department"];

stage.addEventListener(MouseEvent.CLICK, loadCMIFile);

function loadCMIFile(evt:MouseEvent):void {

var cmiRequest:URLRequest = new URLRequest("PARAM.CMI") ;

var cmiLoader:URLLoader = new URLLoader(cmiRequest);

cmiLoader.load(cmiRequest);

cmiLoader.addEventListener(Event.COMPLETE, test);

}

function test(evt:Event):void {

var cmi:Object = {};

try {

cmi = parseCMI(evt.target.data);

}

catch (err:Error) {

trace(err.name,err.message + "\rUnable to parse selected file.");

}

//Then, as an example of how to get at your stuff:

trace(cmi["Lesson_Location"]);

}

private function parseCMI(string:String):Object {

          var obj:Object = {};

          //parse Core

for (var i:int = 0; i < cmiCore.length; i++) {

var regExp:RegExp = new RegExp(cmiCore+"=");

if (regExp.test(string) {

obj[cmiCore] = extract(string, regExp);

}

}

//parse Core_Vendor

for (var j:int = 0; j < cmiVendor.length; j++) {

var regExp:RegExp = new RegExp(cmiVendor+"=");

if (regExp.test(string) {

obj[cmiVendor] = extract(string, regExp);

}

}

//parse Evaluation

for (var j:int = 0; j < cmiEvaluation.length; j++) {

var regExp:RegExp = new RegExp(cmiEvaluation+"=");

if (regExp.test(string) {

obj[cmiEvaluation] = extract(string, regExp);

}

}

//parse Objectives_Status

for (var j:int = 0; j < cmiObjectives.length; j++) {

var regExp:RegExp = new RegExp(cmiObjectives+"=");

if (regExp.test(string) {

obj[cmiObjectives] = extract(string, regExp);

}

}

//parse Student_Demographics

for (var j:int = 0; j < cmiDemographics.length; j++) {

var regExp:RegExp = new RegExp(cmiDemographics+"=");

if (regExp.test(string) {

obj[cmiDemographics] = extract(string, regExp);

}

}

}

private function extract(str:String, re:RegExp):String {

var value:String = str.substr(str.search(re)); //makes a string that starts after the equals sign

value = value.substring(0,value.search(/[\r\n]/); //cuts off the string at the first newline or carriage return

return value;

}

We could use a 2D Vector so we didn't have to parse each section separately, but with just 5 sections I suppose it doesn't matter.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Apr 08, 2013 Apr 08, 2013

Ok I implemented the OCD version, which works great btw! Now that I know that this much is possible, I'm going to work on extracting just the values. Then I need to go back to the TMS and try to figure out exactly what it does and what it needs from Flash. I know two things for sure, it has to read from the cmi file and it has to write back to it. I know writng to a text file is possible using AIR but can it be done using Flash?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Apr 08, 2013 Apr 08, 2013

Absolutely. With Flash, the easiest way I know is with FileReference.

You make a new FileReference and then use the save method. This would save a string to a text file:

var string:String = "whatever";

var fr:FileReference = new FileReference();

fr.save(string, "myTextFile.txt");

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guest
Apr 08, 2013 Apr 08, 2013

I'm surprised it worked, since I didn't take the time to test it beforehand. Usually I have to iron out about 3-5 logical errors before any of my code works.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Apr 08, 2013 Apr 08, 2013
LATEST

There were a few syntax errors but they were easy fixes.

I haven't had time to work on this today but as soon as I do I will start with FileReference and let you know how it works out. Thanks again!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines