Разбор CSV: как NSScanner может распознать пустое поле (т.е.,,)?

Я очень новичок в Xcode и пытаюсь - как миллионы - анализировать файл CSV. Я прочитал много статей и управляю ими, но у меня возникает проблема, когда мой NSScanner перехватывает пустое поле: "Field_A, Field_B,, Field_D". Я предполагаю, что это потому, что он игнорирует пустое пространство по умолчанию, или в этом случае вообще нет места.

Строка это:

"Личный","2011-01-01","Personal","Сигареты",,4,60,"Cash","",

Я попытался отладить его, используя scanLocation:

2011-04-22 15:57:32.414 Spending[42015:a0f] Before while...scan location is:0
2011-04-22 15:57:32.414 Spending[42015:a0f] Account: "Personal" - scan location is:10
2011-04-22 15:57:32.415 Spending[42015:a0f] Date: "2011-01-01" - scan location is:23
2011-04-22 15:57:32.415 Spending[42015:a0f] Category: "Personal" - scan location is:34
2011-04-22 15:57:32.416 Spending[42015:a0f] Subcategory: "Cigarettes" - scan location is:47
2011-04-22 15:57:32.416 Spending[42015:a0f] Income: 4.600000 - scan location is:53
2011-04-22 15:57:32.416 Spending[42015:a0f] Expense: 0.000000 - scan location is:53
2011-04-22 15:57:32.417 Spending[42015:a0f] Payment: "Cash" - scan location is:60
2011-04-22 15:57:32.417 Spending[42015:a0f] Note: "" - scan location is:63

И как вы можете видеть после этого, даже поле расходов не получает значения (должно быть 4.60).

Вот соответствующий фрагмент кода:

NSScanner *scanner = [NSScanner scannerWithString:fileString];
    [scanner setCharactersToBeSkipped: [NSCharacterSet characterSetWithCharactersInString:@"\n, "]];

    NSString *account, *date, *category, *subcategory, *payment, *note;
    float income, expense;

    // Set up data delimiter using comma
    NSCharacterSet *commaSet;
    commaSet = [NSCharacterSet characterSetWithCharactersInString:@","];

    NSLog (@"Before while...scan location is:%d\n", scanner.scanLocation);

    [scanner scanUpToCharactersFromSet:commaSet intoString:&account];
    NSLog(@"Account: %@ - scan location is:%d\n",account, scanner.scanLocation);

    [scanner scanUpToCharactersFromSet:commaSet intoString:&date];
    NSLog(@"Date: %@ - scan location is:%d\n",date, scanner.scanLocation);

    [scanner scanUpToCharactersFromSet:commaSet intoString:&category]; 
    NSLog(@"Category: %@ - scan location is:%d\n",category, scanner.scanLocation);

    [scanner scanUpToCharactersFromSet:commaSet intoString:&subcategory]; 
    NSLog(@"Subcategory: %@ - scan location is:%d\n",subcategory, scanner.scanLocation);

    [scanner scanFloat:&income];
    NSLog(@"Income: %f - scan location is:%d\n",income, scanner.scanLocation);

    [scanner scanFloat:&expense]; 
    NSLog(@"Expense: %f - scan location is:%d\n",expense, scanner.scanLocation);

    [scanner scanUpToCharactersFromSet:commaSet intoString:&payment]; 
    NSLog(@"Payment: %@ - scan location is:%d\n",payment, scanner.scanLocation);

    [scanner scanUpToCharactersFromSet:commaSet intoString:&note];
    NSLog(@"Note: %@\n - scan location is:%d",note, scanner.scanLocation);

Я пытался внимательно изучить NSScanner Class Reference, но не смог понять? У тебя есть?

Спасибо, Фабрицио.

3 ответа

Решение

Причина, по которой сканер не видит ваше пустое поле, заключается в том, что вы указали пропустить запятые. Вы вызвали setCharactersToBeSkipped с набором из 3 символов:

  1. '\ n' Символ новой строки
  2. ',' Запятая
  3. ' ' Космос

Когда вы затем просите сканер "scanFloat", он перебирает любой пропускаемый символ, пока не достигнет десятичного числа. Вот как пустое поле пропускается.

Если вы хотите поймать пустые поля, удалите запятую из набора символов, чтобы пропустить. Затем, каждый раз, когда функция сканирования находит пустое поле, она возвращает NO. Возможно, вам придется увеличить позицию сканирования вручную, когда это произойдет.

Разбор CSV в Objective-C? Это звучит знакомо:

https://github.com/davedelong/CHCSVParser

Отказ от ответственности: я написал это.:)


Для того, что вы делаете, вы можете просто взять файл и запустить что-то вроде +[NSArray arrayWithContentsOfCSVFile:encoding:error:] метод, или вы можете прочитать его в строку и сделать что-то вроде этого:

NSString *csv = @"\"Personal\",\"2011-01-01\",\"Personal\",\"Cigarettes\",,4.60,\"Cash\",\"\",";
NSLog(@"%@", [csv CSVComponents]);

Какие журналы:

2011-04-22 09:51:16.651 CHCSVParser[2658:903] (
        (
        Personal,
        "2011-01-01",
        Personal,
        Cigarettes,
        "",
        "4.60",
        Cash,
        ""
    )
)

(Обратите внимание, что это NSArray из NSArrays из NSStrings)

Если вы беспокоитесь о наращивании памяти, то вы также можете использовать CHCSVParser напрямую и получать информацию через делегата. Он работает почти так же, как NSXMLParser работает.

Взгляните на эту статью о сканерах CSV.

Вот еще одна статья

Решение Дейва Делонга также прекрасно работает.

Итог: CSV кажется тривиальным, но на самом деле это не так, если вы хотите справиться с любым CSV, брошенным на вас изящно.

Другие вопросы по тегам