На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила раздела C/C++: Базы данных
Модераторы: B.V.
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
    > Кирилица из базы данных MySQL выводится в виде краказабр.
      Здравсвуйте!
      При выводи русских символов из базы данных отображаются кракозябры. Подскажите, пожалуйста, как сделать чтобы кирилица отображалась корректно?
      ExpandedWrap disabled
        Подключение установлено успешно!
         
        Результаты на экране:
        ======================
         _group | name       |
        ======================
         1      | ￐ヤ￐ᄉ￐ᄑ￐ᄌ￑チ |
         2      | ￐ロ￑ム￐ᄑ￑マ   |
        ======================


      Пытался указать кодировку подключения к БД вот таким образром:
      ExpandedWrap disabled
         db.executeQuery("SET NAMES 'utf8mb4'");
      , но тогда выходит ошибка: No result available.

      ExpandedWrap disabled
        #include "MySQLConnection.h"
        #include "windows.h"
         
        using namespace school;
         
         
        int main() {
            system("chcp 65001 > nul");
         
           std::string sql_query = "SELECT _group, name FROM learners";
            
            try {
                MySQLConnection db(
                    "localhost",
                    3306,
                    "root",
                    "",
                    "school"
                );
         
                if (db.isConnected()) {
                    std::cout << u8"Подключение установлено успешно!\n\n";
                    db.executeQuery("SET NAMES 'utf8mb4'"); // ← Добавить
         
                    // Выводим результаты на экран
                    std::cout << u8"Результаты на экране:" << std::endl;
                    db.executeQuery(sql_query);
         
                    std::cout << "\n" << std::string(50, '=') << std::endl;
                }
            }
            catch (sql::SQLException& e) {
                std::cerr << "Ошибка MySQL: " << e.what() << std::endl;
            }
         
            return 0;
        }


      ExpandedWrap disabled
        Подключение установлено успешно!
         
        No result available
         
        D:\Programming\VS\source\repos\school\x64\Release\school.exe (процесс 31968) завершил работу с кодом 0 (0x0).
        Нажмите любую клавишу, чтобы закрыть это окно…
      Сообщение отредактировано: DDim1000 -
        Всё не так, дружище!!!

        Если ты хочешь работать с "усечёнными" кодировками (типа только для Европы) - ты можешь использовать в кодировках своих таблиц что-то типа ascii_general_ci или latin1_bin. Но это будет твой первый самый знаковый фэйл!!! Щяс в мире гомоглобальная глоболизация, и если ты проигноришь восточные знаковые таблицы - ты будешь лузером! А мы с тобой не будем =)

        Поэтому так, и только так ты сможешь сохранить все (и своё и чисто западное): кодировка таблиц - строго utf8mb4.

        При этом имеет смысл выбирать collation как utf8mb4_unicode_520_ci. Это последняя из версий сортировок строк по "национальным признакам сортировок".

        И вот тогда, и только тогда, многие из нас (по крайней мере я) помогут тебе с выводом данных из БД... А пока ты будешь пробовать неизвестные нам сеты кодировок - вряд ли кто тебе что-то подскажет. Хотя вероятность ненулевая есть.
          А таблица в базе в какой кодировке?

          Табличку на экране кто рисует? Не вижу кода вывода. Только вывод отчеркушки.
          Сообщение отредактировано: sharky72 -
            Цитата sharky72 @
            А таблица в базе в какой кодировке?

            В utf8_general_ci.

            Добавлено
            Цитата sharky72 @
            Табличку на экране кто рисует? Не вижу кода вывода. Только вывод отчеркушки.

            ExpandedWrap disabled
              namespace school
              {
                  // ================================================= Класс для работы таблицы =====================================================
                  // Реализация методов MyString
                  MyString::MyString() : std::wstring() {}
               
                  MyString::MyString(std::wstring s) : std::wstring(s) {}
               
                  MyString::MyString(const wchar_t* s) : std::wstring(s) {}
               
                  // Явная инстанциация шаблонов
                  template class MyVector<MyString>;
                  template class MyVector<MyVector<MyString>>;
               
                  // Реализация TableFormatter
                  TableFormatter::TableFormatter(size_t& cmax,
                      MyVector<MyVector<MyString>>& data,
                      MyVector<MyString>& header)
                      : m(data), h(header), CMAX(cmax)
                  {
                  }
               
                  void TableFormatter::format()
                  {
                      calculateMaxColumns();
                      resizeRows();
                      addRowNumbers();
                      calculateColumnWidths();
                      alignColumns();
                      alignHeaders();
                  }
               
                  void TableFormatter::calculateMaxColumns()
                  {
                      for (const auto& e : m)
                          if (CMAX < e.size())
                              CMAX = e.size();
                  }
               
                  void TableFormatter::resizeRows()
                  {
                      for (auto& e : m)
                          if (CMAX > e.size()) e.resize(CMAX);
               
                      if (CMAX > h.size()) h.resize(CMAX);
                  }
               
                  void TableFormatter::addRowNumbers()
                  {
                      for (size_t r = 0; r < m.size(); ++r)
                      {
                          m[r][0] = StringConverter::toString(r + 1);
                      }
                  }
               
                  void TableFormatter::calculateColumnWidths()
                  {
                      std::vector<size_t> n(CMAX, 0);
               
                      // Инициализация ширинами заголовков
                      for (size_t i = 0; i < h.size() && i < CMAX; ++i)
                      {
                          n[i] = h[i].size();
                      }
               
                      // Нахождение максимальных ширин
                      for (size_t c = 0; c < CMAX; ++c)
                      {
                          for (size_t r = 0; r < m.size(); ++r)
                          {
                              if (n[c] < m[r][c].size())
                                  n[c] = m[r][c].size();
                          }
                      }
               
                      // Выравнивание столбцов
                      for (size_t c = 0; c < CMAX; ++c)
                      {
                          for (size_t r = 0; r < m.size(); ++r)
                          {
                              if (n[c] > m[r][c].size())
                                  m[r][c].resize(n[c], ' ');
                          }
                      }
               
                      // Выравнивание заголовков
                      for (size_t c = 0; c < CMAX; ++c)
                      {
                          if (h[c].size() < m[0][c].size())
                              h[c].resize(m[0][c].size(), ' ');
                      }
                  }
               
                  void TableFormatter::alignColumns() {}
                  void TableFormatter::alignHeaders() {}
               
                  // Реализация TablePrinter
                  TablePrinter::TablePrinter(
                      size_t& cmax,
                      MyVector<MyVector<MyString>>& data,
                      MyVector<MyString>& header)
                      : CMAX(cmax), m(data), h(header)
                  {
                  }
               
                  void TablePrinter::print(std::wostream& o)
                  {
                      std::wstring line = createLine();
               
                      o << line << '\n';
                      printHeader(o);
                      o << '\n' << line << '\n';
                      printRows(o);
                      o << line << '\n' << '\n';
                  }
               
                  std::wstring TablePrinter::createLine()
                  {
                      std::wstring line;
                      for (size_t c = 0; c < CMAX; ++c)
                      {
                          line += std::wstring(h[c].size() + 3, '=');
                      }
                      return line;
                  }
               
                  void TablePrinter::printHeader(std::wostream& o)
                  {
                      for (size_t c = 0; c < CMAX; ++c)
                      {
                          o << ' ' << h[c] << " |";
                      }
                  }
               
                  void TablePrinter::printRows(std::wostream& o)
                  {
                      for (const auto& r : m)
                      {
                          for (const auto& e : r)
                          {
                              o << ' ' << e << " |";
                          }
                          o << '\n';
                      }
                  }
               
                  // Реализация Tab
                  Tab::Tab() = default;
               
                  MyVector<MyString>& Tab::operator[](const size_t i)
                  {
                      if (i >= m.size()) m.resize(i + 1);
                      return *(m.begin() + i);
                  }
               
                  void Tab::formater()
                  {
                      TableFormatter formatter(CMAX, m, h);
                      formatter.format();
                  }
               
                  // Реализация оператора вывода
                  std::wostream& operator<<(std::wostream& o, Tab& t)
                  {
                      t.formater();
               
                      TablePrinter printer(t.CMAX, t.m, t.h);
                      printer.print(o);
               
                      return o;
                  }
               
                  // Реализация TabTest
                  
                  Tab TabTest::createTabFromResultSet(std::unique_ptr<sql::ResultSet> res)
                  {
                      ///-----------------------|
                      /// Создаём табулятор.    |
                      ///-----------------------:
                      Tab tab;
                      if (!res) return tab;
               
                      try
                      {
                          sql::ResultSetMetaData* meta = res->getMetaData();
                          int columnCount = meta->getColumnCount();
                          
                          // Создаем заголовки
                          for (int i = 1; i <= columnCount; i++)
                          {
                              std::string columnName = meta->getColumnName(i);
                              std::wstring wColumnName(columnName.begin(), columnName.end());
                              tab.head(wColumnName);
                          }
                          
                          // Заполняем данными
                          int row = 0;
                          while (res->next())
                          {
                              for (int col = 1; col <= columnCount; col++)
                              {
                                  std::string value = res->getString(col);
                                  std::wstring wValue(value.begin(), value.end());
                                  tab[row][col - 1] = res->wasNull() ? L"NULL" : wValue;
                              }
                              row++;
                          }
                      }
                      catch (const sql::SQLException& e)
                      {
                          std::wcout << L"Ошибка при создании таблицы: " << e.what() << std::endl;
                      }
               
                      return tab;
                  };
               
               
               
               
               
              // ========================================= Класс для работы с данными базы данных MySQL =========================================
                  MySQLConnection::MySQLConnection(const std::string& host, int port,
                      const std::string& user, const std::string& password,
                      const std::string& database) {
               
                      sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance();
                      std::string connectionString = "tcp://" + host + ":" + std::to_string(port);
               
                      connection.reset(driver->connect(connectionString, user, password));
                      connection->setSchema(database);
                  }
               
                  void MySQLConnection::executeQuery(const std::string& query) {
                      std::unique_ptr<sql::Statement> stmt(connection->createStatement());
                      std::unique_ptr<sql::ResultSet> res(stmt->executeQuery(query));
               
                      TabTest tabTest;
                      Tab resultTab = tabTest.createTabFromResultSet(std::move(res));
                      
                      std::wcout << resultTab; // Вывести когда нужно
                  }
               
                  bool MySQLConnection::isConnected() {
                      return connection.get() != nullptr && !connection->isClosed();
                  }
              }

            Прикреплённая картинка
            Прикреплённая картинка
              ExpandedWrap disabled
                for (int col = 1; col <= columnCount; col++)
                {
                    std::string value = res->getString(col);
                    std::wstring wValue(value.begin(), value.end());
                    tab[row][col - 1] = res->wasNull() ? L"NULL" : wValue;
                }


              Вообще то конвертация utf8 -> utf16 так не работает. Вернее работает, но только для первой половины ASCII таблицы, поэтому названия столбцов у вас выводятся корректно.
              в utf8 один символ может быть представлен последовательностью от 1 до 4 байт, т.е. символ в кодировке имеет переменную длину. Для кириллицы например 2.
              А вы тупо копируете каждый char в wchar_t, т.е. 1 байт просто копируете в 2 (второй будет нулевой). Хотя надо было считать 2 и преобразовать их в Unicode символ.
              Почитайте про utf8. https://ru.wikipedia.org/wiki/UTF-8

              Поищите на развалах функцию конвертации utf8->utf16 и наоборот. Ну или воспользуйтесь системной MultiByteToWideChar и WideCharToMultiByte

              ExpandedWrap disabled
                std::wstring ConvertUtf8ToWide(const std::string& str)
                {
                    int count = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), NULL, 0);
                    std::wstring wstr(count, 0);
                    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), &wstr[0], count);
                    return wstr;
                }
              Сообщение отредактировано: sharky72 -
                Вот еще одну функцию нашел...
                ExpandedWrap disabled
                   std::wstring utf8_to_utf16(const std::string& utf8)
                   {
                       wstring_convert<codecvt_utf8_utf16<char16_t>, char16_t> convert;
                       u16string utf16 = convert.from_bytes(utf8);
                       wstring wstr(utf16.begin(), utf16.end());
                       return wstr;
                   }

                Только я не понял, где их нужно вызывать и что им передавать?
                Сообщение отредактировано: DDim1000 -
                  В моем сообщении процитирован ваш код. Вот его надо заменить. То же самое для названий колонок.
                  У вас база возвращает строки в UTF8. Вам надо их преобразовать в utf16 (wide char) для вывода в консоль. (Которая wide char преобразует в utf8 или другую кодировку консоли ;))

                  ExpandedWrap disabled
                    for (int col = 1; col <= columnCount; col++)
                    {
                        tab[row][col - 1] = res->wasNull() ? L"NULL" : ConvertUtf8ToWide(res->getString(col));
                    }


                  utf8 можно хранить в std::string. Но для хранения строки в std::wstring нужно проводить преобразование кодовой страницы. CP_UTF8 -> CP_UNICODE

                  P.S. utf8 может и прекрасно, как нам вещает majesto, но это эрзац созданный для экономии памяти и у которого дофига подводных камней. Да и вообще CodePage это сплошная головная боль.
                  P.P.S. Кстати учитывайте тот фактор что SET NAMES подразумевает что ВСЕ общение между вами и сервером идет в указанной кодовой странице (см. доку на MySql).
                  Сообщение отредактировано: sharky72 -
                    Цитата sharky72 @
                    В моем сообщении процитирован ваш код. Вот его надо заменить. То же самое для названий колонок.
                    У вас база возвращает строки в UTF8. Вам надо их преобразовать в utf16 (wide char) для вывода в консоль. (Которая wide char преобразует в utf8 или другую кодировку консоли ;))

                    ExpandedWrap disabled
                      for (int col = 1; col <= columnCount; col++)
                      {
                          tab[row][col - 1] = res->wasNull() ? L"NULL" : ConvertUtf8ToWide(res->getString(col));
                      }


                    utf8 можно хранить в std::string. Но для хранения строки в std::wstring нужно проводить преобразование кодовой страницы. CP_UTF8 -> CP_UNICODE

                    P.S. utf8 может и прекрасно, как нам вещает majesto, но это эрзац созданный для экономии памяти и у которого дофига подводных камней. Да и вообще CodePage это сплошная головная боль.
                    P.P.S. Кстати учитывайте тот фактор что SET NAMES подразумевает что ВСЕ общение между вами и сервером идет в указанной кодовой странице (см. доку на MySql).

                    Спасибо, заработало!!!

                    Но есть другой вопрос. В таблиц первый столбец под иминем id, второй столбец - под иминем _group. Столбец под иминем id содержаться значения: 1, а столбец под иминем _group содержит значение 11. Почему при таком запросе:
                    ExpandedWrap disabled
                         std::string sql_query = "SELECT _group FROM learners";

                    выводится такой результат?:

                    ExpandedWrap disabled
                      Подключение установлено успешно!
                       
                      Результаты на экране:
                      =========
                       _group |
                      =========
                       1      |
                      =========


                    То есть, заголовок выводится прввилно, а содержимое выводится первого столбца. Почему так?
                      А вы дебагом сами пройтись можете?
                      Насколько я понимаю запрос должен вернуть один столбец.
                      Что вернулось в res->getString()
                      Вы уверены что вам возвращают именно строку? Если в столбце должно быть число.
                      Короче надо убедится что это данные именно из id. А не затертая 1 в числе 11
                      Сообщение отредактировано: sharky72 -
                        Цитата sharky72 @
                        ...
                        Короче надо убедится что это данные именно из id. А не затертая 1 в числе 11

                        Я изменил значения столбца _group на 66 и 44, и вот что получается.
                        При таком запросе:
                        ExpandedWrap disabled
                          std::string sql_query = "SELECT id, _group FROM learners";

                        Вот такой вывод:
                        ExpandedWrap disabled
                          Подключение установлено успешно!
                           
                          Результаты на экране:
                          ==============
                           id | _group |
                          ==============
                           1  | 66     |
                           2  | 44     |
                          ==============
                           
                           
                          ==================================================
                           
                          D:\Programming\VS\source\repos\school\x64\Release\school.exe (процесс 1308) завершил работу с кодом 0 (0x0).
                          Нажмите любую клавишу, чтобы закрыть это окно:


                        А при таком запросе:
                        ExpandedWrap disabled
                          std::string sql_query = "SELECT _group FROM learners";

                        Вот такой вывод:
                        ExpandedWrap disabled
                          Подключение установлено успешно!
                           
                          Результаты на экране:
                          =========
                           _group |
                          =========
                           1      |
                           2      |
                          =========
                           
                           
                          ==================================================
                           
                          D:\Programming\VS\source\repos\school\x64\Release\school.exe (процесс 8784) завершил работу с кодом 0 (0x0).
                          Нажмите любую клавишу, чтобы закрыть это окно:
                        Сообщение отредактировано: DDim1000 -
                          Не знаю. Я попробовал у себя сделать подобный запрос - и у меня база возвращает один столбец и в нем правильные данные для этого столбца.
                          Выложите свой проект. И опишите параметры вашей таблицы. Попробую собрать и проверить на своем myqsl.
                            Я вот тут посмотрел участки кода от ТС. Сразу скажу, что использование std::wstring для "внутренних" манипуляций не лучшая идея. Обычного std::string для работы с UTF-8 вполне достаточно. Единственное, что нужно помнить - длина std::string в частых случаях не соответствует количеству символов. Ну это специфика UTF-8.

                            Для винды самый простой и быстрый вариант:

                            ExpandedWrap disabled
                              #include <windows.h>
                              #include <string>
                               
                              size_t utf8_count_windows(const std::string& utf8) {
                                  int len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0);
                                  return len > 0 ? len - 1 : 0;
                              }

                            Кроссплатформенный вариант:

                            ExpandedWrap disabled
                              #include <string>
                               
                              size_t utf8_count_cross(const std::string& utf8) {
                                  size_t len = 0;
                                  for (unsigned char c : utf8) if ((c & 0xC0) != 0x80) len++;
                                  return len;
                              }

                            Единственный момент со вторым вариантом: неправильно считает, если в строке встречаются графемы, или строка с невалидным содержимым для UTF-8.
                            Ну а std::wstring нужен только для взаимодействия с Windows API.
                              Цитата sharky72 @
                              Не знаю. Я попробовал у себя сделать подобный запрос - и у меня база возвращает один столбец и в нем правильные данные для этого столбца.
                              Выложите свой проект. И опишите параметры вашей таблицы. Попробую собрать и проверить на своем myqsl.

                              Вот мой проект: https://disk.yandex.ru/d/bgJz7OSEq-lb1g
                              Прикреплённая картинка
                              Прикреплённая картинка

                              Прикреплённая картинка
                              Прикреплённая картинка
                              Сообщение отредактировано: DDim1000 -
                                Ох... Плохо когда слушают советы, но не делают то что говорят.

                                1. VC++ Directories это глобальная вещь. Что то там править нужно только в крайнем случае. Inculde/Lib directories правятся только в c++/link настройках вашего проект
                                2. Используйте макросы Visual Studio! Иначе ваш проект будет собираться ТОЛЬКО на вашем компьютере. Абсолютные пути - зло!
                                3. Если сказано заменить код, то его заменяют, а не оставляют старый и добавляют новый.
                                Должно быть так
                                ExpandedWrap disabled
                                  // Создаем заголовки
                                  for (int i = 1; i <= columnCount; i++)
                                  {
                                      std::string columnName = meta->getColumnName(i);
                                      tab.head(ConvertUtf8ToWide(columnName));
                                  }
                                   
                                  // Заполняем данными
                                  int row = 0;
                                  while (res->next())
                                  {
                                      for (int col = 1; col <= columnCount; col++)
                                      {
                                           std::string value = res->getString(col);
                                           tab[row][col - 1] = res->wasNull() ? L"NULL" : ConvertUtf8ToWide(value);
                                      }
                                      row++;
                                  }


                                4. Проблема в том что вы сами выводите addRowNumbers, а не значения столбцов и как раз эту нумерацию и видите.

                                ExpandedWrap disabled
                                      void TableFormatter::format()
                                      {
                                          calculateMaxColumns();
                                          resizeRows();
                                          //addRowNumbers();
                                          calculateColumnWidths();
                                          alignColumns();
                                          alignHeaders();
                                      }


                                Я немного причесал ваш проект на предмет путей к include и lib
                                В добавок добавил debug варианты mysqlconn и добавил их в правильные пути для запуска включая плагины. Так что теперь можете запускать и Debug вариант.
                                https://disk.yandex.ru/d/YpqYjdmPHLORkw
                                Сообщение отредактировано: sharky72 -
                                  Цитата sharky72 @
                                  Ох... Плохо когда слушают советы, но не делают то что говорят.

                                  1. VC++ Directories это глобальная вещь. Что то там править нужно только в крайнем случае. Inculde/Lib directories правятся только в c++/link настройках вашего проект
                                  2. Используйте макросы Visual Studio! Иначе ваш проект будет собираться ТОЛЬКО на вашем компьютере. Абсолютные пути - зло!
                                  3. Если сказано заменить код, то его заменяют, а не оставляют старый и добавляют новый.
                                  Должно быть так
                                  ExpandedWrap disabled
                                    // Создаем заголовки
                                    for (int i = 1; i <= columnCount; i++)
                                    {
                                        std::string columnName = meta->getColumnName(i);
                                        tab.head(ConvertUtf8ToWide(columnName));
                                    }
                                     
                                    // Заполняем данными
                                    int row = 0;
                                    while (res->next())
                                    {
                                        for (int col = 1; col <= columnCount; col++)
                                        {
                                             std::string value = res->getString(col);
                                             tab[row][col - 1] = res->wasNull() ? L"NULL" : ConvertUtf8ToWide(value);
                                        }
                                        row++;
                                    }


                                  4. Проблема в том что вы сами выводите addRowNumbers, а не значения столбцов и как раз эту нумерацию и видите.

                                  ExpandedWrap disabled
                                        void TableFormatter::format()
                                        {
                                            calculateMaxColumns();
                                            resizeRows();
                                            //addRowNumbers();
                                            calculateColumnWidths();
                                            alignColumns();
                                            alignHeaders();
                                        }


                                  Я немного причесал ваш проект на предмет путей к include и lib
                                  В добавок добавил debug варианты mysqlconn и добавил их в правильные пути для запуска включая плагины. Так что теперь можете запускать и Debug вариант.
                                  https://disk.yandex.ru/d/YpqYjdmPHLORkw

                                  Спасибо большое! Все работает!!!
                                  ExpandedWrap disabled
                                    Подключение установлено успешно!
                                     
                                    Результаты на экране:
                                    ============================================================
                                     _group | name    | sex | age | teleg_id  | teleg_nickname |
                                    ============================================================
                                     44     | Иван    | м   | 75  | XXXXXXXXX | Иванко         |
                                     66     | Николай | м   | 40  | ХХХХХХХХХ | нико           |
                                    ============================================================
                                     
                                     
                                    ==================================================
                                     
                                    D:\Programming\VS\source\repos\school\x64\Release\school.exe (процесс 18024) завершил работу с кодом 0 (0x0).
                                    Нажмите любую клавишу, чтобы закрыть это окно:


                                  А насчет Debug... Да, проект запускаеться, но выходят ошибки.
                                  ExpandedWrap disabled
                                    Unable to connect to
                                     
                                    D:\Programming\VS\source\repos\school\x64\Debug\school.exe (процесс 6620) завершил работу с кодом 0 (0x0).
                                    Нажмите любую клавишу, чтобы закрыть это окно:


                                  ExpandedWrap disabled
                                    Missing option OPT_MULTI_HOST = true
                                     
                                    D:\Programming\VS\source\repos\school\x64\Debug\school.exe (процесс 28784) завершил работу с кодом 0 (0x0).
                                    Нажмите любую клавишу, чтобы закрыть это окно:


                                  ExpandedWrap disabled
                                    Unable to connect to X
                                     
                                    D:\Programming\VS\source\repos\school\x64\Debug\school.exe (процесс 39012) завершил работу с кодом 0 (0x0).
                                    Нажмите любую клавишу, чтобы закрыть это окно:
                                  1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                  0 пользователей:


                                  Рейтинг@Mail.ru
                                  [ Script execution time: 0,0753 ]   [ 19 queries used ]   [ Generated: 3.11.25, 21:09 GMT ]