Как оптимально подсчитать количество элементов в строке?

Рейтинг: 0Ответов: 2Опубликовано: 14.03.2015

Доброго всем дня!

Имеются строки типа:

S:='[123, 2345, ''test'', ''str1, str2'', -1234, ''str3 ''is'' str4'']';

Как элегантно посчитать количество элементов? Может, как-то можно привести к TArray<Variant> и просто считать Length()? Должно получиться 6 в данном случае.

Проблема в том, что мы не можем разделить по разделителям, т.к. ',', так же как и кавычки, может находится внутри строки. А считать в цикле все открывающиеся и закрывающиеся кавычки работает, конечно, но как-то громозко и некрасиво выглядит.

Ответы

▲ 2Принят

@VadimTukaev В Си-шной программе, которую Вы привели, есть небольшая идеологическая ошибка, которая связана с тем, что в паскалевском синтаксисе запись из первоначального условия задачи:

S := '[123, 2345, ''test'', ''str1, str2'', -1234, ''str3 ''is'' str4'']';

означает на самом деле, что S будет содержать:

[123, 2345, 'test', 'str1, str2', -1234, 'str3 'is' str4']

то есть никакого задвоения кавычек (таким способом в Паскале просто разрешается коллизия между обрамляющими и внутренними кавычками - крайние отбрасываются, а удвоенные внутренние - об-одиночиваются). Автор недаром привел строку в условии задачи именно в таком Паскалевском виде - через S := '...' ;

Поэтому, если поправить эту входную строку в Вашей программе в соответствии с исходным заданием, то далее следует внести коррективы и в последующий код и вместо деления по модулю 4 (&& quotes % 4 == 0) проверять остаток от деления на 2 (&& quotes % 2 == 0) - то есть просто "на чётнось" пары кавычек, что в свою очередь позволяет использовать булевский флаг Quotes вместо int Quotes, например вот так:

var
  c : Char;
  s : String = 
  '[123, 2345, ''test'', ''str1, str2'', -1234, ''str3 ''is'' str4'']';
  Chunks : Integer = 1;
  Quotes : Boolean = false;

begin Writeln(s);

  for c in s do 
    case c of
      '''': Quotes := not Quotes;
       ',': if not Quotes then inc(Chunks)
    end;

  Writeln('Chunks = ', Chunks); Readln
end.

Согласитесь, всё же на Паскале-Дельфи Ваша исправленная программа выглядит более изящно! ;))) http://ideone.com/Pc2AtP

@VadimTukaev Я не менял название ВАШИХ переменных сознательно, чтобы не создавать Вам дополнительную интеллектуальную нагрузку во второстепенных вопросах - ведь основной вопрос заключался в том, что Вы решали ДРУГУЮ задачу c ДРУГИМИ исходными данными.

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

Вдогонку - в ответ на Ваше возражение: Хотите с другой переменной, хотите, чтобы количество переменных было как у Вас, хотите, чтобы строка была Си-шная (PChar) - пожалуйста: http://ideone.com/Scnalo

Самое смешное что на Паскале/Делфи подладиться под Си - не проблема, он Паскаль может и так и так! А вот на Си - не всегда. Ну просто потому что Паскаль/Дельфи - язык более высокого уровня, при этом позволяющий спуститься вниз, вплоть до ассемблера или подняться вверх выше С++.

Что касается объявления переменных - то этим и отличаются языки Паскалевской ветки - строгостью во всем, в том числе и в объявлении переменных, что, как известно, приводит к более надежному коду. Недаром в конкурсе на создание единого языка программирования для Пентагона в полуфинал вышли три языка, и все три полуфиналиста основывались на Паскале, а си-образные языки в полуфинал не прошли вообще!

@Isaev Вы меня умиляете! Потом кто-нибудь опять выдаст решение теперь уже для этой, уточненной задачи, Вы снова внесёте уточнения, которые не были известны на предыдущем шаге! Эдак можно продолжать до бесконечности...

Потому что именно так решил Вашу задачу VadimTukaev (лишь только с поправкой на его ошибку в интерпретации исходных данных), а я лишь записал это решение более ясным языком (к тому же тем, на котором Вы спрашивали решение).

Вы или дайте полную РБНФ-грамматику Вашей задачи - чтобы мы сами без Ваших подсказок смогли решить что там ЕЩЁ может быть, а что нет, либо не морочьте себе (и нам) голову.

А вообще, если хотите послушать совет - не изобретайте велосипед, воспользуйтесь уже разработанной системой нотации, в которой все эти вопросы, которые последовательно встают перед Вами, давно решены и систематизированы:

Возьмите, например, Memo-поле, забейте туда последовательно все Ваши строки (те, из которых потом соберется результирующая строка):

123
2345
'test'
'str1, str2'
-1234
str3 'i, s' str4

и сделайте её авто-сборку:

  Caption := Memo1.Lines.DelimitedText;

полюбуйтесь на неё в заголовке формы:

123,2345,'test',"'str1, str2'",-1234,"str3 'i, s' str4"

Уясните, как на самом деле должна выглядеть собранная строка, а потом вновь автоматически разберите её на первоначальные подстроки - во второе Memo-поле (Memo2). Вот полный текст (2 Memo и 2 Buttona):

procedure TForm1.Button1Click(Sender: TObject);
begin
  Caption := Memo1.Lines.DelimitedText;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo2.Lines.DelimitedText := Caption;
end;

Именно по этим правилам сборки/разборки работают всякие системные ini-файлы, загрузки/выгрузки в Excel и т.д., а главное, что Ваша задача, после того как Вы вернётесь в мэйн-стрим, легко автоматизируется стандартными классами и имеет элементарный ответ: Memo2.Lines.Count, т.е. количество строк при разборке строки.

Постарайтесь подогнать под эту форму Вашу изначальную задачу - множество проблем избежите... и получите Профит! - всё уже полностью автоматизировано в Дельфи для неё!

▲ 1

Почему же громоздко и некрасиво? Вы бы показали свой код. Вот, на С выглядит неплохо.

#include <stdio.h>

int main(void)
{
    const char* const str = "[123, 2345, ''test'', ''str1, str2'', -1234, ''str3 ''is'' str4'']";
    int chunks = 1;
    int quotes = 0;
    char* i;
    for (i = (char*)str; *i != '\0'; i++)
    {
        if (*i == '\'')
        {
            quotes++;
        }
        else if (*i == ',' && quotes % 4 == 0)
        {
            chunks++;
        }
    }
    printf("Chunks = %d\n", chunks);
    return 0;
}