Russian Qt Forum
Ноябрь 24, 2024, 06:12 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 ... 5   Вниз
  Печать  
Автор Тема: Парсинг строк  (Прочитано 32402 раз)
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« : Март 09, 2016, 07:43 »

Привет, друзья!
Есть файл, который содержит строки типа

Цитировать
*node(363484,232.7625,-113.996,40.69956,0,0,0,0,0)
здесь первая цифра - номер узла, три следующие - его координаты.

Написал код, который считывает строку, но выглядит он коряво на мой взгляд.

Код
C++ (Qt)
   while (!fileStream_.atEnd()){
       QString line(fileStream_.readLine());
       if (line == "END NODES"){
           break;
       }
 
       Q_ASSERT(line.startsWith("*node("));
       QTextStream lineStream(&line, QIODevice::ReadOnly);
 
       lineStream.seek(6);
 
       ParsedNode node;
       lineStream >> node.number;
 
       float x;
       float y;
       float z;
 
       lineStream.seek(lineStream.pos() + 1);
       lineStream >> x;
       lineStream.seek(lineStream.pos() + 1);
       lineStream >> y;
       lineStream.seek(lineStream.pos() + 1);
       lineStream >> z;
 
       node.coordinates = QVector3D(x, y, z);
       nodes << node;
   }
 
В регулярках я не разбирался. Быть может есть более "чистые" способы чтения? Прошу помочь.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #1 : Март 09, 2016, 07:55 »

Написал код, который считывает строку, но выглядит он коряво на мой взгляд.
Да и работать будет только в идиальных условиях. Поставь кто пробелов и все развлится. Улыбающийся
Регулярки будут сами проверять, что бы в начале была node и значения разделены запятыми.
А еще есть boost.spirit. Улыбающийся
Записан
Bepec
Гость
« Ответ #2 : Март 09, 2016, 08:04 »

А ещё есть QString::split Веселый
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #3 : Март 09, 2016, 08:29 »

А ещё есть QString::split Веселый
Ща бомбить будет, ЕВПОЧЯ Улыбающийся

Спирит сложная штука, вроде. Наверное за день не разберусь. Но спасибо. А регулярками как это решить?
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #4 : Март 09, 2016, 08:50 »

А регулярками как это решить?
Я не большой специалист по регуляркам, но вот набросок:

« Последнее редактирование: Март 09, 2016, 08:52 от Old » Записан
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« Ответ #5 : Март 09, 2016, 08:58 »

А ещё есть QString::split Веселый

А почему мы и нет? Улыбающийся
Код
C++ (Qt)
std::tuple<float, float, float> parse_coords(QString text) {
   const QStringList& parts = text.replace("*node(", "").replace(")", "").split(",");
   return std::make_tuple(parts[1].toFloat(), parts[2].toFloat(), parts[3].toFloat());
}
 
QString text = "*node(363484,232.7625,-113.996,40.69956,0,0,0,0,0)";
std::tuple<float, float, float> coord = parse_coords(text);
qDebug() << std::get<0>(coord) << std::get<1>(coord) << std::get<2>(coord);
 
Записан

__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #6 : Март 09, 2016, 11:52 »

gil9red, классный вариант. Здесь же можно и ликвидацию пробелов добавить и проверку на количество полученных данных.
Old, спасибо за набросок. Для меня он выглядит страшно Улыбающийся так как не пользовался ранее регулярками. Думаю, пойду по пути, gil9red.
Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #7 : Март 09, 2016, 12:48 »

А регулярками как это решить?
Я не большой специалист по регуляркам, но вот набросок:
я бы так сделал: ссылка (если в координатах дробная часть гарантирована, можно еще упростить)

можно даже не захватывать отдельные результаты, а просто взять и полное совпадение сплитнуть по ,
« Последнее редактирование: Март 09, 2016, 12:49 от kambala » Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Март 09, 2016, 17:15 »

Ща бомбить будет, ЕВПОЧЯ Улыбающийся
Конечно буду Улыбающийся Во всех ответах прослеживается прямо какое-то "физиологическое" желание написать короче - нет, даже КАК МОЖНО КОРОЧЕ! Вот это, дескать, крутизна! Улыбающийся Не самому там чего-то велосипедить, а тыц готовое - и все дела.

А давайте по-взрослому - не просто так "распарсить", а с полным контролем ошибок и предъявлением юзеру текста ошибки (и где она произошла).  
Код
C++ (Qt)
#include <QStringRef>
#include <QDebug>
 
namespace QStringParser {
 
inline QStringRef BeforeFirst( const QStringRef & src, const QString & splitter )
{
return src.left(src.indexOf(splitter));
}
 
inline QStringRef AfterFirst( const QStringRef & src, const QString & splitter )
{
int pos = src.indexOf(splitter);
iif (pos < 0) return src.right(0);
return src.mid(pos + splitter.size());
}
 
inline bool ReadInt( QStringRef & ref, const QString & splitter, int & val )
{
bool ok;
val = BeforeFirst(ref, splitter).trimmed().toInt(&ok);
if (!ok) return false;
ref = AfterFirst(ref, splitter);
return true;
}
 
inline bool ReadFloat( QStringRef & ref, const QString & splitter, float & val )
{
bool ok;
val = BeforeFirst(ref, splitter).trimmed().toFloat(&ok);
if (!ok) return false;
ref = AfterFirst(ref, splitter);
return true;
}
 
} // namespace
 
using namespace QStringParser;
 
bool Error( const QStringRef & ref, QString & err, const QString & txt )
{
err = "\nError at position " + QString::number(ref.position()) + ": " + txt +
 "\n" + ref.trimmed().toString();
return false;
}
 
bool Parse( const QString & srcStr, int & number, float val[3], QString & err )
{
static const QString prefix("node");
static const QString comma(",");
 
QStringRef src(&srcStr);
if (!src.startsWith(prefix))
return Error(src, err, "string must start with 'node'");
 
src = AfterFirst(QStringRef(&srcStr), prefix).trimmed();
if (!src.startsWith('('))
return Error(src, err, "'(' expected");
src = src.mid(1);
 
if (!ReadInt(src, comma, number))
return Error(src, err, "integer value expected");
 
for (int i = 0; i < 3; ++i)
if (!ReadFloat(src, comma, val[i]))
return Error(src, err, "float value expected");
 
return true;
}
 
int main(int argc, char **argv)
{
int nodeNum;
float val[3] = { 0 };
 
QString err;
QString test("node  ( 363484   ,232.7625,    -113.996, 40.69x956,0,0,0,0,0)");
bool ok = Parse(test, nodeNum, val, err);
qDebug() << ok << nodeNum << val[0] << val[1] << val[2];
if (!ok) qDebug() << err;
return 0;
}
Не надо бояться что (в первый раз) придется потратиться на полезные ф-ции, очень быстро это окупится
« Последнее редактирование: Март 09, 2016, 18:40 от Igors » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #9 : Март 09, 2016, 17:34 »

А давайте по-взрослому - не просто так "распарсить", а с полным контролем ошибок и предъявлением юзеру текста ошибки (и где она произошла).  
В большинстве случаев это избыточно, пользователю достаточно указать только номер не валидной строки.
А в этом случае, регулярки легко и универсально позволяют как проверять корректность строки, так и получать значения. Причем они позволят разбирать гораздо более сложные случаи. В отличие от...
А если уж контроль ошибок стоит на первом месте, то лучше взять тот-же spirit.
« Последнее редактирование: Март 09, 2016, 17:50 от Old » Записан
Bepec
Гость
« Ответ #10 : Март 09, 2016, 18:19 »

Распарсить строку и написать класс, делающий это это разные вещи.

PS преждевременная оптимизация, так помоему называется то, что демонстрирует Igors.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Март 09, 2016, 18:59 »

Конечно недаром придумали "регулярки", и недаром они так популярны. Да и спырыт мне очень понравился. Но "в большинстве случаев это избыточно", все можно решить куда проще написав неск ф-ций "по руке" и закинув их в утили. С оптимизацией проблем нет - ну юзаем другой Qt класс, и все. И чего ото козырять туплями, заряжать на строку целый QTextStream и.т.п ?

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

Да, по скорости там нет желающих посравнивать?  Улыбающийся

Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #12 : Март 09, 2016, 19:12 »

Распарсит три числа задачка действительно детская. Были бы вместо чисел номера телефонов в любых видах или паспортные данные, вы бы велосипед еще долго писали (и не факт, что доделали бы). А пользователи регулярок уже давно бы задачу решали. Улыбающийся
« Последнее редактирование: Март 09, 2016, 19:29 от Old » Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #13 : Март 09, 2016, 22:56 »

Поставь кто пробелов и все развлится. Улыбающийся
Как в воду глядите Улыбающийся Полдня потратил на отладку из-за этого. Предполагал, что формат без пробелов.

В итоге реализовал так:

Код
C++ (Qt)
   while (!fileStream_.atEnd()){
       QString line(fileStream_.readLine());
       if (line == endNodesGroupString){
           break;
       }
 
       if (line.startsWith(nodeIndicatorString)){
           const QStringList &lineParts = line.remove(nodeIndicatorString).remove(" ").remove(")").split(',');
           constexpr int partCountRequried = 4;
           if (lineParts.count() < partCountRequried){
               qCritical() << "Got bad node line";
           }
           else{
 
               ParsedNode node;
               node.number = lineParts.at(0).toFloat();
 
               for (int k = 0; k < nodeDimension; ++k){
                   node.coordinates[k] = lineParts.at(k + 1).toFloat();
               }
               nodes << node;
           }
       }
   }
 

Пришлось заменить хранение координат с QVector3D на std::array<float, 3>.
Критикуем, улучшаем (читабельность), пожалуйста Улыбающийся
Думаю, блок else заменить на метод readNode(QStringList) и  добавить к qCritical строку ошибки. Номер линии как-то лениво считать. Или есть какие средства QTextStream позволяющие выводить номер линии?
Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4747



Просмотр профиля WWW
« Ответ #14 : Март 10, 2016, 12:23 »

ага, а потом вместо пробелов попадутся табы и опять все развалится Улыбающийся а в регулярках есть \s*
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
Страниц: [1] 2 3 ... 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.153 секунд. Запросов: 22.