How to get width and height of an image in PB - height

I'm using PB8.0 and I want to get the width and height of a picture to resize my picture in DW. Do you know how to get it?
I search an example to get width and height of a bitmap file. It seems correct with bitmap file but not for other such as .jpg, .gif
Please help me
//Getting a Bitmaps Width and Height
int li_job
blob b
string ls_ext
ls_ext = lower(Right(as_filename, 3))
CHOOSE CASE ls_ext
CASE 'bmp'
li_job = FileOpen( "filename.bmp", StreamMode!, Read! )
IF li_job > 0 THEN
FileRead( li_job, b )
arl_Width = Long(Integer( BlobMid( b, 19, 2 ) ), &
Integer( BlobMid( b, 21, 2) ) )
arl_Height = Long(Integer( BlobMid( b, 23, 2 ) ), &
Integer( BlobMid( b, 25, 2) ) )
FileClose( li_job )
ELSE
RETURN -1
END IF
CASE ELSE
RETURN -1
END CHOOSE
RETURN 1
Thanks

The offset of the image size depends on the file format. The values that your are using are valid for the BMP format, but will be different for another format.
You need to search separately for any of the file formats you need to handle. Some infos are available here. A similar example for VB to handle JPEG, GIF, BMP, & PNG is available here.
Also, you could get some info via the Windows image API, but I have no example available.

One way is to add a Picture control to a window with visible property unchecked and OriginalSize checked.
Set the picture name to load the image and Height and Width will be available to do your processing.
// p_1 is not visible
p_1.PictureName = "c:\temp\pbHowTo.jpg"
MessageBox("height", p_1.height)
MessageBox("width", p_1.width)
You may need to convert the values in pixel since they are in PbUnit.
height_pixel = UnitsToPixels(p_1.height, YUnitsToPixels!)
width_pixel = UnitsToPixels(p_1.width, XUnitsToPixels!)
The Picture control supports .BMP, GIF, .JPG and PNG files.

Here are the code to get the width and height of JPG:
//JPG
li_file = FileOpen(as_file_name, StreamMode!, Read! )
If li_file = -1 Then Return 0
li_read = FileRead (li_file, lb_data)
If String (BlobMid (lb_Data, 1,3)) = Char (255) + Char (216) + Char (255) Then //JPG
ll_DataLen = Len(lb_Data)
ll_DataPos = 3
ll_FilePos = 3
lb_LoopFlag = true
Do While lb_LoopFlag
ll_DataPos = ll_DataPos + 1
ll_FilePos = ll_FilePos + 1
li_Char1 = Asc (String (BlobMid(lb_Data, ll_DataPos, 1)))
li_Char2 = Asc (String (BlobMid(lb_Data, ll_DataPos + 1, 1)))
If li_Char1 = 255 And li_Char2 <> 255 Then
If li_Char2 >= 192 And li_Char2 <= 195 Then
al_width = Asc (String (BlobMid (lb_Data, ll_DataPos + 7,1))) * 256 + Asc (String (BlobMid (lb_Data, ll_DataPos + 8,1)))
al_height= Asc (String (BlobMid (lb_Data, ll_DataPos + 5,1))) * 256 + Asc (String (BlobMid (lb_Data, ll_DataPos + 6,1)))
lb_LoopFlag= False
Else
ll_FilePos = ll_FilePos + Asc (String (BlobMid(lb_Data, ll_DataPos + 3, 1))) + Asc (String (BlobMid(lb_Data, ll_DataPos + 2, 1))) + 1
If ll_FilePos > ll_FileLength Then
Exit
Else
FileSeek(li_file, ll_FilePos)
FileRead(li_file, lb_Data)
ll_DataLen = Len(lb_Data)
ll_DataPos = 0
End If
End If
End If
If ll_DataPos = ll_DataLen - 9 And lb_LoopFlag = True Then
ll_FilePos = ll_FilePos - 9
FileSeek(li_file, ll_FilePos)
FileRead(li_file, lb_Data)
ll_DataLen = Len(lb_Data)
ll_DataPos = 0
End If
If ll_FilePos >= ll_FileLength Then
lb_LoopFlag = False
End If
Loop
End If
For GIF:
al_width = Asc(String (BlobMid (lb_Data, 7,1))) + Asc (String (BlobMid (lb_Data, 8,1))) * 256
al_height = Asc (String (BlobMid (lb_Data, 9,1))) + Asc (String (BlobMid (lb_Data, 10,1))) * 256

Related

Google Sheets multiple search and replace from a list with repeated strings

I've implemented the solution at
Google Sheets multiple search and replace from a list for a similar issue.
I'm using the script
function preg_quote( str ) {
// http://kevin.vanzonneveld.net
// + original by: booeyOH
// + improved by: Ates Goral (http://magnetiq.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// * example 1: preg_quote("$40");
// * returns 1: '\$40'
// * example 2: preg_quote("*RRRING* Hello?");
// * returns 2: '\*RRRING\* Hello\?'
// * example 3: preg_quote("\\.+*?[^]$(){}=!<>|:");
// * returns 3: '\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:'
return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1");
}
function ARRAYREPLACE(input,fromList,toList,caseSensitive){
/* default behavior it is not case sensitive */
if( caseSensitive == undefined ){
caseSensitive = false;
}
/* if the from list it is not a list, become a list */
if( typeof fromList != "object" ) {
fromList = [ fromList ];
}
/* if the to list it is not a list, become a list */
if( typeof toList != "object" ) {
toList = [ toList ];
}
/* force the input be a string */
var result = input.toString();
/* iterates using the max size */
var bigger = Math.max( fromList.length, toList.length) ;
/* defines the words separators */
var arrWordSeparator = [ ".", ",", ";", " " ];
/* interate into the lists */
for(var i = 0; i < bigger; i++ ) {
/* get the word that should be replaced */
var fromValue = fromList[ ( i % ( fromList.length ) ) ]
/* get the new word that should replace */
var toValue = toList[ ( i % ( toList.length ) ) ]
/* do not replace undefined */
if ( fromValue == undefined ) {
continue;
}
if ( toValue == undefined ) {
toValue = "";
}
/* apply case sensitive rule */
var caseRule = "g";
if( !caseSensitive ) {
/* make the regex case insensitive */
caseRule = "gi";
}
/* for each end word char, make the replacement and update the result */
for ( var j = 0; j < arrWordSeparator.length; j++ ) {
/* from value being the first word of the string */
result = result.replace( new RegExp( "^(" + preg_quote( fromValue + arrWordSeparator[ j ] ) + ")" , caseRule ), toValue + arrWordSeparator[ j ] );
/* from value being the last word of the string */
result = result.replace( new RegExp( "(" + preg_quote( arrWordSeparator[ j ] + fromValue ) + ")$" , caseRule ), arrWordSeparator[ j ] + toValue );
/* from value in the middle of the string between two word separators */
for ( var k = 0; k < arrWordSeparator.length; k++ ) {
result = result.replace(
new RegExp(
"(" + preg_quote( arrWordSeparator[ j ] + fromValue + arrWordSeparator[ k ] ) + ")" ,
caseRule
),
/* need to keep the same word separators */
arrWordSeparator[ j ] + toValue + arrWordSeparator[ k ]
);
}
}
/* from value it is the only thing in the string */
result = result.replace( new RegExp( "^(" + preg_quote( fromValue ) + ")$" , caseRule ), toValue );
}
/* return the new result */
return result;
}
An example input is;
"
DETAILS
halter neck style sleeveless detachable
waist tie gathered tiered skirt panels invisible
back zip back neck opening with button close
unlined SIZING
model is 170cm and wears a size 8 garment flat
measurements: size 8 length - 139cm, waist - 33cm,
bust - 43cm size 10 length - 141cm, waist - 35cm, bust -
45cm size 12 length - 143cm, waist - 37cm, bust - 47cm
size 14 length - 145cm, waist - 38cm, bust - 48cm
model stats: bust - 76cm, waist - 57cm, hips - 91cm
GARMENT CARE material - rayon
cold hand wash separately
"
and the replacement list is at;
https://docs.google.com/spreadsheets/d/1MxaZ1Jki1fnVw6jfFllur2BpPN6-6b6fAmLSKQniG-8/edit#gid=0
However my results are only showing a conversion of some of the strings. Below is the output;
"
DETAILS
halter neck style sleeveless detachable
waist tie gathered tiered skirt panels invisible
back zip back neck opening with button close
unlined SIZING
model is 5' 6"" and wears a size 8 garment flat
measurements: size 8 length - 54.5"", waist - 13"",
bust - 43cm size 10 length - 55.5"", waist - 14"", bust -
45cm size 12 length - 56.5"", waist - 14.5"", bust -
47cm size 14 length - 57"", waist - 15"", bust - 48cm
model stats: bust - 30"", waist - 22.5"", hips - 91cm
GARMENT CARE
material - rayon cold hand wash separately
"
The third instance of the dimensions on each line is being ignored, as are the sizes entirely. Could anyone tell me if the script limited to a certain number of replacements, or any changes I should make to replace the sizes? Thanks in advance.
After testing the code the problems seems to be with the values at the end of a line. If you have your input defined as a multi-line JavaScript string make sure that you leave a space after those values.
var input = "\
DETAILS\
\
halter neck style\
sleeveless\
detachable waist tie\
gathered tiered skirt panels\
invisible back zip\
back neck opening with button close\
unlined\
SIZING\
\
model is 170cm and wears a size 8\
garment flat measurements:\
size 8 length - 139cm, waist - 33cm, bust - 43cm \ //Notice the space after "cm"
size 10 length - 141cm, waist - 35cm, bust - 45cm \
size 12 length - 143cm, waist - 37cm, bust - 47cm \
size 14 length - 145cm, waist - 38cm, bust - 48cm \
model stats: bust - 76cm, waist - 57cm, hips - 91cm \
GARMENT CARE\
\
material - rayon\
cold hand wash separately\
";
If you are reading this input through a file, probably the problem is related to a new line (\n) or a carriage return (\r), I would add those values in the arrWordSeparator variable to take care of multi-line inputs. So it would look something similar to this:
/* defines the words separators */
var arrWordSeparator = [ ".", ",", ";", " ", "\n", "\r" ];

Conversion of sql statemnt in Dax

I have a sql Statement with multiple cases now I have to write the statement in Dax
I tried using switch but the output is not same
the sql statement :
CASE WHEN #STRIKER!='' OR #NONSTRIKER!='' THEN (RUNS + (CASE WHEN BYES = 0 AND LEGBYES = 0 THEN OVERTHROW ELSE 0 END))
WHEN #BOWLER!='' THEN (RUNS + WIDE+ (CASE WHEN BALL.NOBALL > 0 AND (BALL.BYES > 0 OR BALL.LEGBYES > 0) THEN BALL.BYES + BALL.NOBALL + BALL.LEGBYES
WHEN BALL.NOBALL > 0 AND BALL.BYES = 0 AND BALL.LEGBYES = 0 THEN BALL.NOBALL
ELSE 0 END) + (CASE WHEN BYES = 0 AND LEGBYES = 0 THEN OVERTHROW ELSE 0 END))
ELSE GRANDTOTAL END ) AS RUNS
First, I would simplify the case statement,
I assume you have the case statements because NULLS can exist in your result? you could simply try
RUNS +
WIDE +
ISNULL(BALL.NOBALL,0) +
ISNULL(BALL.BYES,0) +
ISNULL(BALL.LEGBYES,0) +
ISNULL(OVERTHROW,0)
But if you've already got the model in PowerBI with those columns listed, you could simply add a calculated column that would sum all the columns which would ignore the nulls...
Total = RUNS + WIDE + NOBALL + BYES + LEGBYES + OVERTHROW
? but without the schema, its really hard to know what the solution might be.
good luck!

vlookup function rounding up in vba

Does it exist a way to avoid getting a rounded up result when using vlookup formula in vba. Here is my code:
Sub test()
Dim lastrow, pressurecolumn, lastcolumn As String, total As Integer, x, row, irow, column As Double
lastrow = Range("B8").End(xlDown).Value + 7
Range("Pump_design[Total Pipe losses from plantroom]").ClearContents
For Each row In Columns("EZ")
For irow = 8 To lastrow
total = 0
For column = 4 To 153
x = Cells(irow, column).Value
If Not IsEmpty(x) Then
total = total + Application.WorksheetFunction.VLookup(x, Sheets("Pump Design").Range("Pump_design"), 154, False)
End If
Next column
Cells(irow, "EZ") = Round(total, 4)
If Cells(irow, "EZ") = 0 Then Cells(irow, "EZ").ClearContents
Next irow
row = irow + 1
Next row
End Sub
Just found the trick get total as double instead of integer

Informix 9.52C1 - replace carriage return and line feed

I search google and couldn't find anything that really helped me IBM website show Informix 11.5 and up functions, like if they have stop supporting 9.52C1 or something. Hence the reason I am here.
As the topic stated I'm using Informix 9.52C1. I got this information using the SQL:
SELECT owner FROM systables WHERE TABNAME= ' VERSION';
I'm not sure if the replace function is even supported because when I execute the statement:
SELECT col1, REPLACE('r','p','poster') as col2 FROM table;
The column col2 contains just the letter 'r' however no error was thrown although I used the REPLACE() function.
I know the escape characters (CRLF) can be found using the "\r\n" escape format because this SQL worked accordingly:
SELECT * FROM table WHERE col1 LIKE '%\r\n%';
Note: I used the above SQL to retrieve the version number as I only had access via a client DB application. If however you have access to the actual host running the informix DB server I'm sure there is some command arguement to resolve the version maybe the defualt linux --version or -v arguement with the db server application.
And this indeed returned only the records that contained CRLF.
My main job is to place a backslash in front of CR and LF as I am migrating data from Informix 9.52C1 to a PostgreSQL database. I'm planning to use the COPY function to load the data to the PostgreSQL database and the COPY function works in this manner as I have done a test record. My dilemma is extracting the data from Informix in the correct format. Can anyone assist with this issue?
I have tried:
SELECT REPLACE('\\\r\\\n','\r\n',col1) as description FROM table;
However this didn't work I believe due to the replace function, as I have mention I am not sure if the replace function is available in this Informix version.
Thanks in advance,
jerg
P.S. None of the functions ASCII(),CHAR() and CHR() worked either. Some sites suggested these function. But as far as I can see the functions CHR() & ASCII() was implemented in verison 11.5+. This post suggested the functions.
To migrate data between databases you can use JDBC drivers (I use Jython, but Java or other language which can use JDBC driver will be ok) and then SELECT ... from source database (Informix) and INSERT ... just read data into destination (PostgreSQL). With BatchInsert or PreparedStatement it is really fast. In my code it looks like:
insert_str = 'INSERT INTO ' + table_name + ' (' + column_names + ') VALUES (' + question_marks + ')'
insert_stmt = db_to.prepareStatement(insert_str)
...
pstm2 = db_from.createStatement()
rs_in = pstm2.executeQuery('SELECT %s FROM %s %s' % (column_names, table_name, order_str))
...
while (rs_in.next()):
for i in range(1, col_count + 1):
insert_stmt.setObject(i, rs_in.getObject(i))
Of course you must take care of errors, auto commit, fetch size etc. but I think it is worth your effort.
Your idea with COPY ... is also good. Do you save output from Informix in text file? If it is in text file you can simply correct text file before importing it to PostgreSQL.
I don't recognize the version 9.52C1. An Informix version might be 9.52.UC1 — with a choice from U, F, W, H, T for the first letter. But then again, the main version numbers were 9.00..9.03, 9.10..9.16, 9.20, 9.21, 9.30, 9.40, 10.00, 11.10, 11.50, 11.70, 12.10 — a sequence which doesn't include a 9 and a 5 in a single version. Well, in many ways, it doesn't matter; any version 9.x has been out of support for most of a decade, if not longer, and shouldn't still be in use.
Assuming that an upgrade to a modern version of Informix is not in the cards, you are stuck with implementing your own functions. To do the job, you'd do best to implement them in C and install them as a package of UDRs (user-defined routines) in a shared library. That would give you the best performance. Assuming that's not feasible (I don't have a set of such functions on hand, but it shouldn't be hard to write them), then you have to fall back on SPL (stored procedure language) routines. These are not fast because of the limitations of SPL w.r.t substring operations — specifically, the built-in substring operations with [] notation do not accept variables as subscripts. That's really painful, to be polite about it.
Here's a version of char_at which fetches a single character at a given position in a string of up to 255 characters:
-- #(#)$Id: char_at.spl,v 1.1 1999/05/17 23:59:59 jleffler Exp $
--
-- CHAR_AT stored procedure, to return character at given position in string
--
-- Author: J Leffler
-- Date: 1999-05-17
CREATE PROCEDURE char_at(str VARCHAR(255), pos SMALLINT) RETURNING CHAR(1);
DEFINE c CHAR(1);
IF pos > LENGTH(str) OR pos <= 0 THEN
LET c = NULL;
ELIF pos <= 16 THEN
IF pos = 1 THEN LET c = str[ 1];
ELIF pos = 2 THEN LET c = str[ 2];
ELIF pos = 3 THEN LET c = str[ 3];
ELIF pos = 4 THEN LET c = str[ 4];
ELIF pos = 5 THEN LET c = str[ 5];
ELIF pos = 6 THEN LET c = str[ 6];
ELIF pos = 7 THEN LET c = str[ 7];
ELIF pos = 8 THEN LET c = str[ 8];
ELIF pos = 9 THEN LET c = str[ 9];
ELIF pos = 10 THEN LET c = str[10];
ELIF pos = 11 THEN LET c = str[11];
ELIF pos = 12 THEN LET c = str[12];
ELIF pos = 13 THEN LET c = str[13];
ELIF pos = 14 THEN LET c = str[14];
ELIF pos = 15 THEN LET c = str[15];
ELIF pos = 16 THEN LET c = str[16];
END IF;
ELIF pos <= 32 THEN LET c = char_at(str[ 17, 32], pos - 1 * 16);
ELIF pos <= 48 THEN LET c = char_at(str[ 33, 48], pos - 2 * 16);
ELIF pos <= 64 THEN LET c = char_at(str[ 49, 64], pos - 3 * 16);
ELIF pos <= 80 THEN LET c = char_at(str[ 65, 80], pos - 4 * 16);
ELIF pos <= 96 THEN LET c = char_at(str[ 81, 96], pos - 5 * 16);
ELIF pos <= 112 THEN LET c = char_at(str[ 97,112], pos - 6 * 16);
ELIF pos <= 128 THEN LET c = char_at(str[113,128], pos - 7 * 16);
ELIF pos <= 144 THEN LET c = char_at(str[129,144], pos - 8 * 16);
ELIF pos <= 160 THEN LET c = char_at(str[145,160], pos - 9 * 16);
ELIF pos <= 176 THEN LET c = char_at(str[161,176], pos - 10 * 16);
ELIF pos <= 192 THEN LET c = char_at(str[177,192], pos - 11 * 16);
ELIF pos <= 208 THEN LET c = char_at(str[193,208], pos - 12 * 16);
ELIF pos <= 224 THEN LET c = char_at(str[209,224], pos - 13 * 16);
ELIF pos <= 240 THEN LET c = char_at(str[225,240], pos - 14 * 16);
ELIF pos <= 255 THEN LET c = char_at(str[241,255], pos - 15 * 16); -- Note asymmetry in upper bound!
ELSE LET c = NULL; -- Not reached!
END IF;
RETURN c;
END PROCEDURE;
Handling general substrings and the like is similarly painful. I've never gone to the effort of building even a semi-performant SPL implementation of SUBSTR. It is far, far better to upgrade to a version of Informix that has the support built-in.
CHR() and ASCII()
The CHR(). ASCII() functions can be simulated with:
-- #(#)$Id: chr.sql,v 1.2 2008/09/19 18:48:37 jleffler Exp $
--
-- #(#)Procedure CHR() - return character corresponding to integer
CREATE PROCEDURE chr(i INTEGER) RETURNING CHAR(1) AS result;
DEFINE c CHAR;
IF i < 0 OR i > 255 THEN
RAISE EXCEPTION -746, 0, 'CHR(): integer value out of range 0..255';
END IF;
IF i = 0 OR i IS NULL THEN
LET c = NULL;
ELSE
SELECT chr INTO c FROM ascii WHERE val = i;
END IF;
RETURN c;
END PROCEDURE;
-- #(#)$Id: ascii.sql,v 1.2 2008/09/19 18:40:19 jleffler Exp $
--
-- Procedure ASCII - returning integer corresponding to character.
-- Misnomer: it works on any single-byte character.
CREATE PROCEDURE jl_ascii(C CHAR) RETURNING INT AS result;
DEFINE i INTEGER;
IF c IS NULL THEN
LET i = 0;
ELSE
SELECT val INTO i FROM ascii WHERE chr = c;
END IF;
RETURN i;
END PROCEDURE;
which requires a table:
-- #(#)$Id: asciitbl.sql,v 1.2 2005/03/30 17:51:12 jleffler Exp $
--
-- #(#)Create ASCII Table (for ASCII and CHR functions).
CREATE TABLE ascii
(
val INTEGER NOT NULL UNIQUE CONSTRAINT u1_ascii,
chr CHAR(1) NOT NULL UNIQUE CONSTRAINT u2_ascii
);
REVOKE ALL ON ascii FROM PUBLIC;
GRANT SELECT ON ascii TO PUBLIC;
and the data to go in it — 255 lines such as:
…
32|
33|!
34|"
35|#
36|$
37|%
38|&
39|'
40|(
…
64|#
65|A
66|B
67|C
68|D
69|E
…
You should be able to find a copy of this code at the IIUG under the name ascii.

Split string of digits into individual cells, including digits within parentheses/brackets

I have a column where each cell has a string of digits, ?, -, and digits in parentheses/brackets/curly brackets. A good example would be something like the following:
3????0{1012}?121-2[101]--01221111(01)1
How do I separate the string into different cells by characters, where a 'character' in this case refers to any number, ?, -, and value within the parentheses/brackets/curly brackets (including said parentheses/brackets/curly brackets)?
In essence, the string above would turn into the following (spaced apart to denote a separate cell):
3 ? ? ? ? 0 {1012} ? 1 2 1 - 2 [101] - - 0 1 2 2 1 1 1 1 (01) 1
The amount of numbers within the parentheses/brackets/curly brackets vary. There are no letters in any of the strings.
Here you are!
RegEx method:
Sub Test_RegEx()
Dim s, col, m
s = "3????0{1012}?121-2[101]--01221111(01)1"
Set col = CreateObject("Scripting.Dictionary")
With CreateObject("VBScript.RegExp")
.Global = True
.Pattern = "(?:\d|-|\?|\(\d+\)|\[\d+\]|\{\d+\})"
For Each m In .Execute(s)
col(col.Count) = m
Next
End With
MsgBox Join(col.items) ' 3 ? ? ? ? 0 {1012} ? 1 2 1 - 2 [101] - - 0 1 2 2 1 1 1 1 (01) 1
End Sub
Loop method:
Sub Test_Loop()
Dim s, col, q, t, k, i
s = "3????0{1012}?121-2[101]--01221111(01)1"
Set col = CreateObject("Scripting.Dictionary")
q = "_"
t = True
k = 0
For i = 1 To Len(s)
t = (t Or InStr(1, ")]}", q) > 0) And InStr(1, "([{", q) = 0
q = Mid(s, i, 1)
If t Then k = k + 1
col(k) = col(k) & q
Next
MsgBox Join(col.items) ' 3 ? ? ? ? 0 {1012} ? 1 2 1 - 2 [101] - - 0 1 2 2 1 1 1 1 (01) 1
End Sub
Something else to look at :)
Sub test()
'String to parse through
Dim aStr As String
'final string to print
Dim finalString As String
aStr = "3????0{1012}?121-2[101]--01221111(01)1"
'Loop through string
For i = 1 To Len(aStr)
'The character to look at
char = Mid(aStr, i, 1)
'Check if the character is an opening brace, curly brace, or parenthesis
Dim result As String
Select Case char
Case "["
result = loop_until_end(Mid(aStr, i + 1), "]")
i = i + Len(result)
result = char & result
Case "("
result = loop_until_end(Mid(aStr, i + 1), ")")
i = i + Len(result)
result = char & result
Case "{"
result = loop_until_end(Mid(aStr, i + 1), "}")
i = i + Len(result)
result = char & result
Case Else
result = Mid(aStr, i, 1)
End Select
finalString = finalString & result & " "
Next
Debug.Print (finalString)
End Sub
'Loops through and concatenate to a final string until the end_char is found
'Returns a substring starting from the character after
Function loop_until_end(aStr, end_char)
idx = 1
If (Len(aStr) <= 1) Then
loop_until_end = aStr
Else
char = Mid(aStr, idx, 1)
Do Until (char = end_char)
idx = idx + 1
char = Mid(aStr, idx, 1)
Loop
End If
loop_until_end = Mid(aStr, 1, idx)
End Function
Assuming the data is in column A starting in row 1 and that you want the results start in column B and going right for each row of data in column A, here is alternate method using only worksheet formulas.
In cell B1 use this formula:
=IF(OR(LEFT(A1,1)={"(","[","{"}),LEFT(A1,MIN(FIND({")","]","}"},A1&")]}"))),IFERROR(--LEFT(A1,1),LEFT(A1,1)))
In cell C1 use this formula:
=IF(OR(MID($A1,SUMPRODUCT(LEN($B1:B1))+1,1)={"(","[","{"}),MID($A1,SUMPRODUCT(LEN($B1:B1))+1,MIN(FIND({")","]","}"},$A1&")]}",SUMPRODUCT(LEN($B1:B1))+1))-SUMPRODUCT(LEN($B1:B1))),IFERROR(--MID($A1,SUMPRODUCT(LEN($B1:B1))+1,1),MID($A1,SUMPRODUCT(LEN($B1:B1))+1,1)))
Copy the C1 formula right until it starts giving you blanks (there are no more items left to split out from the string in the A cell). In your example, need to copy it right to column AA. Then you can copy the formulas down for the rest of your Column A data.