Load EC-Lab Data File
hrodstein
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
// About "Load EC-Lab Data File" procedures
// To use this procedure, execute a command such as:
// LoadECLabDataFile(pathName, filePath)
// where
// String pathName // Name of an Igor symbolic path or ""
// String filePath // Name of file or full path to file
// If you are not familiar with Igor symbolic paths, execute:
// DisplayHelpTopic "Symbolic Paths"
// LoadECLabDataFile automatically determines the name line, first data line, and delimiter character.
// If it gets those parameters wrong, you can use LoadECLabDataFile2 instead. With LoadECLabDataFile2,
// you specify those parameters in addition to the pathName and filePath.
// These procedures solve issues raised by the column names in EC-Lab data files.
// There are two categories of wave names in Igor: standard and liberal. Standard names start
// with a letter and can include letters, numbers, and the underscore character. Liberal names
// can include any character except for single-quote, double-quote, colon, and semicolon.
// For further discussion of standard and liberal names, execute:
// DisplayHelpTopic "Object Names"
// Programming with liberal names is tricky and most Igor procedures do not work with
// liberal names. Consequently, LoadWaves, by default, "cleans up" column names to produce
// standard wave names. Using LoadWave/B, you can load waves with liberal names but this
// is usually a bad idea because liberal names cause issues down the road unless you are
// very careful.
// The EC-Lab data file from Bio-Logic uses column names that contain characters that
// are not legal in Igor standard names. Examples are "<Ewe>/V" and "|Ewe|/V". Furthermore,
// when LoadWaves cleans these up, they both evaluate to X_Ewe__V, causing a name conflict.
// And, there are some EC-Lab data files that contain columns with the exact same name
// more than once - see https://www.wavemetrics.com/node/21003.
// The LoadECLabDataFile procedure handles these issues by removing characters that
// are not legal in standard Igor names and then by making sure that each name is unique.
// As a result, you wind up with waves named, for example, Ewe_V and Ewe_V1.
//
// Here is a more extensive, though not complete, list of wave names produced by LoadECLabDataFile:
// freq_Hz, ReZ_Ohm, ImZ_Ohm, Z_Ohm, PhaseZ_deg, time_s, Ewe_V, I_mA
// Cs_uF, Cp_uF, cyclenumber, IRange, Ewe_V1, I_A, Q_Qo_mA_h, ReY_Ohm_1
// ImY_Ohm_1, Y_Ohm_1, PhaseY_deg, I_mA1, dq_mA_h, x1
static Function/S MakeNameUnique(name, nameList)
String name // Name that we are about to add to /B flag
String nameList // List of names already added to /B flag
String originalName = name
Variable index = 0
do
if (WhichListItem(name, nameList) < 0)
break // name is not in the list
endif
index += 1
sprintf name, "%s%d", originalName, index // Generate new name by appending a number
while(1)
return name
End
// RemoveIllegalCharacters(name)
// Removes characters that we know are not legal in Igor standard names.
// Here are typical resulting wave names:
// freq_Hz, ReZ_Ohm, ImZ_Ohm, Z_Ohm, PhaseZ_deg, time_s, Ewe_V, I_mA
// Cs_uF, Cp_uF, cyclenumber, IRange, Ewe_V1, I_A, Q_Qo_mA_h, ReY_Ohm_1
// ImY_Ohm_1, Y_Ohm_1, PhaseY_deg, I_mA1, dq_mA_h, x1
static Function/S RemoveIllegalCharacters(name)
String name
name = ReplaceString("|", name, "")
name = ReplaceString("<", name, "")
name = ReplaceString(">", name, "")
name = ReplaceString("(", name, "")
name = ReplaceString(")", name, "")
name = ReplaceString("/", name, "_")
name = ReplaceString(" ", name, "")
return name
End
Function/S GenerateColumnInfoStr(pathName, filePath, nameLine, delimiter)
String pathName // Name of an Igor symbolic path or ""
String filePath // Name of file or full path to file
Variable nameLine // 0-based number of line containing column names
String delimiter // Typically "," or "\t" (tab)
Variable refNum
Open/R/P=$pathName refNum as filePath
Variable lineNumber = 0
do
String text
FReadLine refNum, text
if (lineNumber == nameLine)
break
endif
lineNumber += 1
while(1)
text = ReplaceString("\r", text, "") // Remove any CR terminator character
text = ReplaceString("\n", text, "") // Remove any LF terminator character
Close refNum
String nameList = "" // Accumulates names
String columnInfoStr = ""
Variable numNames = ItemsInList(text, delimiter)
Variable index
for(index=0; index<numNames; index+=1)
String name = StringFromList(index, text, delimiter)
if (strlen(name) == 0)
name = "_skip_"
endif
// Remove characters that we know are not legal in Igor standard names
name = RemoveIllegalCharacters(name)
// Make sure this is a legal standard wave name
name = CleanupName(name, 0)
// Make the name unique if it conflicts with a name already added to nameList
name = MakeNameUnique(name, nameList)
columnInfoStr += "N='" + name + "';"
nameList += name + ";"
endfor
return columnInfoStr
End
// GetFileToLoad(pathName, filePath)
// If necessary, displays an Open File dialog to allow user to choose the file.
// Returns -1 if cancelled, 0 if OK.
// If the function result is 0 then, on return, pathName and filePath are suitable
// for use by LoadWave.
static Function GetFileToLoad(pathName, filePath)
String& pathName // Input and output: Name of an Igor symbolic path or ""
String& filePath // Input and output: Name of file or full path to file
if ((strlen(pathName)==0) || (strlen(filePath)==0))
// Display dialog looking for file.
String fileFilters = "Text Files (*.txt):.txt;"
fileFilters += "CSV Files (*.csv):.csv;"
fileFilters += "DAT Files (*.dat):.dat;"
fileFilters += "All Files:.*;"
Variable refNum
Open/D/R/F=fileFilters/P=$pathName refNum as filePath
filePath = S_fileName // S_fileName is full path set by Open/D
if (strlen(filePath) == 0) // User cancelled?
return -1
endif
endif
return 0
End
Function LoadECLabDataFile2(pathName, filePath, nameLine, firstDataLine, delimiter)
String pathName // Name of an Igor symbolic path or ""
String filePath // Name of file or full path to file
Variable nameLine // Zero-based line number of names line
Variable firstDataLine // Zero-based line number of first data line
String delimiter // Delimiter between names and numbers, typically "," or "\t" (tab)
// First get a valid reference to a file
Variable result = GetFileToLoad(pathName, filePath)
if (result != 0)
return result // User cancelled Open File dialog
endif
String columnInfoStr = GenerateColumnInfoStr(pathName, filePath, nameLine, delimiter)
// Print columnInfoStr
LoadWave/J/Q/L={nameLine,firstDataLine,0,0,0}/A/B=columnInfoStr/E=1/P=$pathName filePath
return 0
End
static Function FindFirstDataLineAndDelimiter(pathName, filePath, firstDataLine, delimiter)
String pathName // Name of an Igor symbolic path or ""
String filePath // Name of file or full path to file
Variable& firstDataLine // Output
String& delimiter // Output
Variable refNum = 0
Open/R/P=$pathName refNum as filePath
if (refNum == 0)
return -1 // Unexpected error
endif
Variable lineNum = 0
do
String text
FReadLine refNum, text
if (strlen(text) == 0)
Close refNum
return -1 // Did not find first data line - Should not happen if this is a valid EC-Lab file
endif
Variable num
sscanf text, "%g", num
if (V_Flag == 1)
// The line starts with a number
firstDataLine = lineNum
String junk
sscanf text, "%[^\t ,]", junk // Deposit all characters other than tab, space, or comma into junk
int len = strlen(junk)
delimiter = text[len]
break
endif
lineNum += 1
while(1)
Close refNum
return 0
End
// LoadECLabDataFile(pathName, filePath)
// Automatically determines the name line, first data line, and delimiter,
// and then calls LoadECLabDataFile2.
// It is assumed that the first line that starts with a valid number is the first
// data line and that the line before the first data line is the name line.
Function LoadECLabDataFile(pathName, filePath)
String pathName // Name of an Igor symbolic path or ""
String filePath // Name of file or full path to file
// First get a valid reference to a file
Variable result = GetFileToLoad(pathName, filePath)
if (result != 0)
return result // User cancelled Open File dialog
endif
Variable nameLine = 0 // Zero-based line number of names line
Variable firstDataLine = 0 // Zero-based line number of first data line
String delimiter = "" // Delimiter between names and numbers, typically "," or "\t" (tab)
result = FindFirstDataLineAndDelimiter(pathName, filePath, firstDataLine, delimiter)
if (result != 0)
return result // Should not happen if this is a valid EC-Lab file
endif
nameLine = firstDataLine - 1
// For debugging only
// Printf "Name line = %d, first data line = %d, delimiter = %d\r", nameLine, firstDataLine, char2num(delimiter)
String columnInfoStr = GenerateColumnInfoStr(pathName, filePath, nameLine, delimiter)
// Print columnInfoStr
LoadWave/J/Q/L={nameLine,firstDataLine,0,0,0}/A/B=columnInfoStr/E=1/P=$pathName filePath
return 0
End
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
// About "Load EC-Lab Data File" procedures
// To use this procedure, execute a command such as:
// LoadECLabDataFile(pathName, filePath)
// where
// String pathName // Name of an Igor symbolic path or ""
// String filePath // Name of file or full path to file
// If you are not familiar with Igor symbolic paths, execute:
// DisplayHelpTopic "Symbolic Paths"
// LoadECLabDataFile automatically determines the name line, first data line, and delimiter character.
// If it gets those parameters wrong, you can use LoadECLabDataFile2 instead. With LoadECLabDataFile2,
// you specify those parameters in addition to the pathName and filePath.
// These procedures solve issues raised by the column names in EC-Lab data files.
// There are two categories of wave names in Igor: standard and liberal. Standard names start
// with a letter and can include letters, numbers, and the underscore character. Liberal names
// can include any character except for single-quote, double-quote, colon, and semicolon.
// For further discussion of standard and liberal names, execute:
// DisplayHelpTopic "Object Names"
// Programming with liberal names is tricky and most Igor procedures do not work with
// liberal names. Consequently, LoadWaves, by default, "cleans up" column names to produce
// standard wave names. Using LoadWave/B, you can load waves with liberal names but this
// is usually a bad idea because liberal names cause issues down the road unless you are
// very careful.
// The EC-Lab data file from Bio-Logic uses column names that contain characters that
// are not legal in Igor standard names. Examples are "<Ewe>/V" and "|Ewe|/V". Furthermore,
// when LoadWaves cleans these up, they both evaluate to X_Ewe__V, causing a name conflict.
// And, there are some EC-Lab data files that contain columns with the exact same name
// more than once - see https://www.wavemetrics.com/node/21003.
// The LoadECLabDataFile procedure handles these issues by removing characters that
// are not legal in standard Igor names and then by making sure that each name is unique.
// As a result, you wind up with waves named, for example, Ewe_V and Ewe_V1.
//
// Here is a more extensive, though not complete, list of wave names produced by LoadECLabDataFile:
// freq_Hz, ReZ_Ohm, ImZ_Ohm, Z_Ohm, PhaseZ_deg, time_s, Ewe_V, I_mA
// Cs_uF, Cp_uF, cyclenumber, IRange, Ewe_V1, I_A, Q_Qo_mA_h, ReY_Ohm_1
// ImY_Ohm_1, Y_Ohm_1, PhaseY_deg, I_mA1, dq_mA_h, x1
static Function/S MakeNameUnique(name, nameList)
String name // Name that we are about to add to /B flag
String nameList // List of names already added to /B flag
String originalName = name
Variable index = 0
do
if (WhichListItem(name, nameList) < 0)
break // name is not in the list
endif
index += 1
sprintf name, "%s%d", originalName, index // Generate new name by appending a number
while(1)
return name
End
// RemoveIllegalCharacters(name)
// Removes characters that we know are not legal in Igor standard names.
// Here are typical resulting wave names:
// freq_Hz, ReZ_Ohm, ImZ_Ohm, Z_Ohm, PhaseZ_deg, time_s, Ewe_V, I_mA
// Cs_uF, Cp_uF, cyclenumber, IRange, Ewe_V1, I_A, Q_Qo_mA_h, ReY_Ohm_1
// ImY_Ohm_1, Y_Ohm_1, PhaseY_deg, I_mA1, dq_mA_h, x1
static Function/S RemoveIllegalCharacters(name)
String name
name = ReplaceString("|", name, "")
name = ReplaceString("<", name, "")
name = ReplaceString(">", name, "")
name = ReplaceString("(", name, "")
name = ReplaceString(")", name, "")
name = ReplaceString("/", name, "_")
name = ReplaceString(" ", name, "")
return name
End
Function/S GenerateColumnInfoStr(pathName, filePath, nameLine, delimiter)
String pathName // Name of an Igor symbolic path or ""
String filePath // Name of file or full path to file
Variable nameLine // 0-based number of line containing column names
String delimiter // Typically "," or "\t" (tab)
Variable refNum
Open/R/P=$pathName refNum as filePath
Variable lineNumber = 0
do
String text
FReadLine refNum, text
if (lineNumber == nameLine)
break
endif
lineNumber += 1
while(1)
text = ReplaceString("\r", text, "") // Remove any CR terminator character
text = ReplaceString("\n", text, "") // Remove any LF terminator character
Close refNum
String nameList = "" // Accumulates names
String columnInfoStr = ""
Variable numNames = ItemsInList(text, delimiter)
Variable index
for(index=0; index<numNames; index+=1)
String name = StringFromList(index, text, delimiter)
if (strlen(name) == 0)
name = "_skip_"
endif
// Remove characters that we know are not legal in Igor standard names
name = RemoveIllegalCharacters(name)
// Make sure this is a legal standard wave name
name = CleanupName(name, 0)
// Make the name unique if it conflicts with a name already added to nameList
name = MakeNameUnique(name, nameList)
columnInfoStr += "N='" + name + "';"
nameList += name + ";"
endfor
return columnInfoStr
End
// GetFileToLoad(pathName, filePath)
// If necessary, displays an Open File dialog to allow user to choose the file.
// Returns -1 if cancelled, 0 if OK.
// If the function result is 0 then, on return, pathName and filePath are suitable
// for use by LoadWave.
static Function GetFileToLoad(pathName, filePath)
String& pathName // Input and output: Name of an Igor symbolic path or ""
String& filePath // Input and output: Name of file or full path to file
if ((strlen(pathName)==0) || (strlen(filePath)==0))
// Display dialog looking for file.
String fileFilters = "Text Files (*.txt):.txt;"
fileFilters += "CSV Files (*.csv):.csv;"
fileFilters += "DAT Files (*.dat):.dat;"
fileFilters += "All Files:.*;"
Variable refNum
Open/D/R/F=fileFilters/P=$pathName refNum as filePath
filePath = S_fileName // S_fileName is full path set by Open/D
if (strlen(filePath) == 0) // User cancelled?
return -1
endif
endif
return 0
End
Function LoadECLabDataFile2(pathName, filePath, nameLine, firstDataLine, delimiter)
String pathName // Name of an Igor symbolic path or ""
String filePath // Name of file or full path to file
Variable nameLine // Zero-based line number of names line
Variable firstDataLine // Zero-based line number of first data line
String delimiter // Delimiter between names and numbers, typically "," or "\t" (tab)
// First get a valid reference to a file
Variable result = GetFileToLoad(pathName, filePath)
if (result != 0)
return result // User cancelled Open File dialog
endif
String columnInfoStr = GenerateColumnInfoStr(pathName, filePath, nameLine, delimiter)
// Print columnInfoStr
LoadWave/J/Q/L={nameLine,firstDataLine,0,0,0}/A/B=columnInfoStr/E=1/P=$pathName filePath
return 0
End
static Function FindFirstDataLineAndDelimiter(pathName, filePath, firstDataLine, delimiter)
String pathName // Name of an Igor symbolic path or ""
String filePath // Name of file or full path to file
Variable& firstDataLine // Output
String& delimiter // Output
Variable refNum = 0
Open/R/P=$pathName refNum as filePath
if (refNum == 0)
return -1 // Unexpected error
endif
Variable lineNum = 0
do
String text
FReadLine refNum, text
if (strlen(text) == 0)
Close refNum
return -1 // Did not find first data line - Should not happen if this is a valid EC-Lab file
endif
Variable num
sscanf text, "%g", num
if (V_Flag == 1)
// The line starts with a number
firstDataLine = lineNum
String junk
sscanf text, "%[^\t ,]", junk // Deposit all characters other than tab, space, or comma into junk
int len = strlen(junk)
delimiter = text[len]
break
endif
lineNum += 1
while(1)
Close refNum
return 0
End
// LoadECLabDataFile(pathName, filePath)
// Automatically determines the name line, first data line, and delimiter,
// and then calls LoadECLabDataFile2.
// It is assumed that the first line that starts with a valid number is the first
// data line and that the line before the first data line is the name line.
Function LoadECLabDataFile(pathName, filePath)
String pathName // Name of an Igor symbolic path or ""
String filePath // Name of file or full path to file
// First get a valid reference to a file
Variable result = GetFileToLoad(pathName, filePath)
if (result != 0)
return result // User cancelled Open File dialog
endif
Variable nameLine = 0 // Zero-based line number of names line
Variable firstDataLine = 0 // Zero-based line number of first data line
String delimiter = "" // Delimiter between names and numbers, typically "," or "\t" (tab)
result = FindFirstDataLineAndDelimiter(pathName, filePath, firstDataLine, delimiter)
if (result != 0)
return result // Should not happen if this is a valid EC-Lab file
endif
nameLine = firstDataLine - 1
// For debugging only
// Printf "Name line = %d, first data line = %d, delimiter = %d\r", nameLine, firstDataLine, char2num(delimiter)
String columnInfoStr = GenerateColumnInfoStr(pathName, filePath, nameLine, delimiter)
// Print columnInfoStr
LoadWave/J/Q/L={nameLine,firstDataLine,0,0,0}/A/B=columnInfoStr/E=1/P=$pathName filePath
return 0
End
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
This procedure illustrates how to clean up wave names and prevent duplicate names when loading a file, such as the EC-Labs data file from Bio-Logic, that uses characters that are not legal in standard Igor names.
See the comments in the procedures above for details.
October 8, 2019 at 07:03 am - Permalink
The version committed today automatically skips header lines.
In this version, the LoadECLabDataFile function takes only two parameters: pathName and filePath. It automatically determines nameLine, firstDataLine, and delimiter based on two assumptions: that the first line which starts with a valid number is the firstDataLine and that the nameLine is the line before the firstDataLine.
The procedures include a LoadECLabDataFile2 function which is the same as the old LoadECLabDataFile function and takes five parameters: pathName, filePath, nameLine, firstDataLine, delimiter. LoadECLabDataFile2 is for use only if LoadECLabDataFile does not automatically find the correct nameLine, firstDataLine, and delimiter parameters.
October 17, 2019 at 07:51 am - Permalink
I would like to used EC-lab macro for opening my EC-lab data. But unfortunately due to the error of "int" command in the following line I couldn't compile the macro
int len = strlen(junk)
could you please tell me how can I manage this error?
July 1, 2020 at 10:28 am - Permalink
The "int" declaration was added in Igor version 7. What version are you using?
July 1, 2020 at 01:05 pm - Permalink
thanks for reply. I am using version 6.3. How can I replace "int" in Igor version 6.3?
July 3, 2020 at 05:12 am - Permalink