views:

528

answers:

7

I have a text box field inputs 123,145,125 I to separate this field into an array of integers. And validate this field true or false if everything is parsed right.

CODE:

private bool chkID(out int[] val) 
{
    char[] delimiters = new char[] { ',' };
    string[] strSplit = iconeID.Text.Split(delimiters);  


    int[] intArr = null;
    foreach (string s in strSplit) //splits the new parsed characters 
    {
        int tmp;
        tmp = 0;
        if (Int32.TryParse(s, out tmp))
        {
            if (intArr == null)
            {
                intArr = new int[1];
            }
            else
            {
                Array.Resize(ref intArr, intArr.Length + 1);
            }
            intArr[intArr.Length - 1] = tmp;
        }

        if (Int32.TryParse(iconeID.Text, out tmp))
        {
            iconeID.BorderColor = Color.Empty;
            iconeID.BorderWidth = Unit.Empty;

            tmp = int.Parse(iconeID.Text);
            val = new int[1];
            val[0] = tmp;
            return true;
        }


    }
    val = null;
    iconeID.BorderColor = Color.Red;
    iconeID.BorderWidth = 2;
    return false;
}

//new Code: private bool chkID(out int[] val) //bool satus for checkIconeID function { string[] split = srtID.Text.Split(new char[1] {','}); List numbers = new List(); int parsed;

  bool isOk = true;
  foreach( string n in split){
   if(Int32.TryParse( n , out parsed))
    numbers.Add(parsed);
   else
    isOk = false;
  }
  if (isOk){
   strID.BorderColor=Color.Empty;
   strID.BorderWidth=Unit.Empty;
   return true;
  } else{
   strID.BorderColor=Color.Red;
   strID.BorderWidth=2;
   return false;
  }
   return numbers.ToArray();
  }
+4  A: 

The given function seems to do too much. Here's one that answers the question implied by your title:

//int[] x = SplitStringIntoInts("1,2,3, 4, 5");

static int[] SplitStringIntoInts(string list)
{
    string[] split = list.Split(new char[1] { ',' });
    List<int> numbers = new List<int>();
    int parsed;

    foreach (string n in split)
    {
        if (int.TryParse(n, out parsed))
            numbers.Add(parsed);
    }

    return numbers.ToArray();
}

EDIT (based on your comment on the question)

You've defined the three things this function needs to do. Now you just need to create methods for each. Below are my guesses for how you could implement them.

int[] ValidateIDs(int[] allIDs)
{
    List<int> validIDs = new List<int>(allIDs);

    //remove invalid IDs

    return validIDs.ToArray();
}

void DownloadXmlData(int[] ids)
{
    ...
}

Now you just execute your new functions:

void CheckIconeID(string ids)
{
    int[] allIDs = SplitStringIntoInts(ids);
    int[] validIDs = ValidateIDs(allIDs);

    DownloadXmlData(validIDs);
}
Austin Salonen
+1  A: 

It might be worth your while to check out this FileHelper and also CSV Reader

Hope they will help you... Take care, Tom

tommieb75
But the question is asking how to parse a CSV string from a form field, not a CSV *file*.
LukeH
Ok, Make a text stream from the string input, accordingly to the FileHelpers util, there is a method ReadString which is part of the CommonEngine Class...see the prototype below...public static object[] ReadString( Type recordClass, string input);
tommieb75
Actually, disregard the first few words up to accordingly..I'll repeat again, my bad! accordingly to the FileHelpers util, there is a method ReadString which is part of the CommonEngine Class...see the prototype below... public static object[] ReadString( Type recordClass, string input );
tommieb75
+1  A: 

There is a good free library for parsing CSV files: FileHelpers

    using FileHelpers;

    // First declare the record class

    [Delimitedrecord(";")]
    public class SampleType
    {
        public string Field1;
        public int    Field2;
    }


    public void ReadExample()
    {
        FileHelperEngine engine = new FileHelperEngine(typeof(SampleType));

        SampleType[] records;    

        records = (SampleType[]) engine.ReadFile("source.txt");

        // Now "records" array contains all the records in the
        // sourcefile and can be acceded like this:

        int sum = records[0].Field2 + records[1].Field2;
    }
But the question is asking how to parse a CSV string from a form field, not a CSV *file*.
LukeH
We need to guess a bigger picture...maybe he's reading the file maybe not...
A: 

This is simple and I think works pretty well. It only return valid numbers:

static int[] SplitStringIntoInts(string list)
{            
    int dummy;            
    return (from x in list.Split(',')
            where int.TryParse(x.ToString(), out dummy)
            select int.Parse(x.ToString())).ToArray();           
}
David Elizondo
Sorry, this won't work. "from x in list" means that each iteration will involve a single char in the string 'list'. So if I run test case "10,20" I will get int[]{1,0,2,0} - not the desired result.
Kirk Broadhurst
@Kirk: That's easily remedied by changing it to `from x in list.Split(',')`.
LukeH
@David: But why call `ToList` followed by `ToArray`? Just `ToArray` on its own will do the job.
LukeH
I reworked it a little bit (Split(',')). It's tinier than the other solutions. Why the downvote? :(
David Elizondo
I don't like the call to `TryParse` immediately followed by a repeated call to `Parse`. I understand that you did that to compress code, but I don't think it's a good idea in general. Either use a statement lambda with a single `TryParse` that properly captures parse result, or just use `Parse` and catch exception (if list is large, and is generally not expected to hold incorrect values, the perf hit is negligible, but the code will be more concise).
Pavel Minaev
@Pavel fair enough. I won't refute the opinion of a 17.5k guy :)
David Elizondo
So what about "private bool checkID(out int[] val)"...how do you make it true if "123,456,789" is correct or false if "123,456,abc," false.The only thing I would like for this function to do is return true of flase. thank you Chad
Chad Sellers
A: 
public bool ParseAndCheck(string source,
    out IList<int> goodItems, out IList<string> badItems)
{
    goodItems = new List<int>();
    badItems = new List<string>();

    foreach (string item in source.Split(','))
    {
        int temp;
        if (int.TryParse(item, out temp))
            goodItems.Add(temp);
        else
            badItems.Add(item);
    }

    return (badItems.Count < 1);
}
LukeH
A: 

I really wanted to comment on @Austin Salonen's answer, but it didn't fit. It is a great answer for the question asked, but i wanted to expand the discussion a bit more generally on csv/int conversion part.

  • It's small point, not worth much debate but I would consider swapping the foreach loop for a plain for loop. You'll likely end up with simpler IL (read faster). See (http://www.codeproject.com/KB/cs/foreach.aspx, http://msdn.microsoft.com/en-us/library/ms973839.aspx [Use For Loops for String Iteration—version 1]).
  • I would create two methods -- one that is safe and uses TryParse and only adds the "good" values, another that is not as safe, but faster.

Proposed "safe" function (with overload in case you don't want to know the bad values)...

    public static int[] SplitAsIntSafe (this string csvString) {
        List<string> badVals;
        return SplitAsIntSafe(csvString, ',', out badVals);
    }
    public static int[] SplitAsIntSafe (this string delimitedString, char splitChar, out List<string> badVals) {
        int         parsed;
        string[]    split   = delimitedString.Split(new char[1] { ',' });
        List<int>   numbers = new List<int>();
        badVals             = new List<string>();

        for (var i = 0; i < split.Length; i++) {
            if (int.TryParse(split[i], out parsed)) {
                numbers.Add(parsed);
            } else {
                badVals.Add(split[i]);
            }
        }
        return numbers.ToArray();
    }

Proposed "fast" function ....

    public static int[] SplitAsIntFast (this string delimitedString, char splitChar) {
        string[]    strArray = delimitedString.Split(splitChar);
        int[]       intArray = new int[strArray.Length];

        if(delimitedString == null) {
            return new int[0];
        }
        for (var i = 0; i < strArray.Length; i++) {
            intArray[i] = int.Parse(strArray[i]);
        }
        return intArray;
    }

Anyway, hope this helps someone.

EBarr
Failure shouldn't be silent, thus I don't like your "safe" version. Modelling it after `TryParse` itself, with results written out to a parameter, and return value indicating whether a parse error occurred, would my a much better choice in my opinion. (The result parameter could either be an `out string[]`, but I'd suggest having the caller provide a `List<string>` to which the parseable columns elements are added. Alternatively, return an `int?[]`, which retains information about *which* columns failed to parse.
Ben Voigt
@Ben, I think you make too broad a statement. It depends on what you want. If you ALREADY KNOW you have mixed values and just want the ints the version I posted accomplishes that goal. If you are trying to validate input and WANT TO KNOW if bad input is coming in then I agree with you. In the later case I don't think i would do a bool return value. It provides me some information, but possibly not enough. I would return the int[] of good entries and use an optional output param of `string[] Bad` so you can show your users the bad values rather than leave him/her to search the string.
EBarr
A: 

In .NET 2.0 you could write

string test = "123,14.5,125,151,1.55,477,777,888";

bool isParsingOk = true;


int[] results = Array.ConvertAll<string,int>(test.Split(','), 
    new Converter<string,int>(
        delegate(string num)
        {
            int r;
            isParsingOk &= int.TryParse(num, out r);
            return r;
        }));
Florian