Russian Qt Forum

Qt => Интернационализация, локализация => Тема начата: prefetch от Декабрь 01, 2008, 11:59



Название: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 11:59
Доброго времени суток.
Возникла проблема с записью кириллицы в ini файл с кодировкой Windows-1251.
Для записи в ini файл использую QSettings, версия Qt 4.4.3
Перепробывал различные варианты, но всегда получаю либо "???????" либо что-то вида
"\x34\x12\x43". Причем в одном из варантов записанная строка "\x34\x53\x23"
представляет собой коды кириллической строки в кодировке Windows-1251.
Английский текст записывается без проблем.
Кроме того, что интересно, если в ini файле есть кириллические строки, но я их не трогаю,
а изменяю другое поле, то они тоже перекодировываються в виде "\x34\x54".
Код:
Код:
QTextCodec *codec = QTextCodec::codecForName( "Windows-1251" );
QLineEdit* ModemName = new QLineEdit;
QSettings* ModemSet = new QSettings("config.txt", QSettings::IniFormat);
ModemName->setText(codec->toUnicode(ModemSet->value("ModemName", "").toByteArray())); //нормально считывает
/.../
ModemSet->setValue("ModemName", ModemName->text().toLocal8Bit().data()); //вот тут загвоздка


Название: Re: Проблема с записью в ini файл
Отправлено: spirit от Декабрь 01, 2008, 15:13
а вот так не пробовали сделать?
Код
C++ (Qt)
QTextCodec::setCodecForLocale(QTextCodec::codecForName("cp-1251"));
 


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 15:41
а вот так не пробовали сделать?
Код
C++ (Qt)
QTextCodec::setCodecForLocale(QTextCodec::codecForName("cp-1251"));
 

Попробовал, никаких изменений. Все равно записывается в виде кодов "\x54\x23".  :(


Название: Re: Проблема с записью в ini файл
Отправлено: spirit от Декабрь 01, 2008, 15:44
а локаль какая?


Название: Re: Проблема с записью в ini файл
Отправлено: BRE от Декабрь 01, 2008, 15:58
Доброго времени суток.
Возникла проблема с записью кириллицы в ini файл с кодировкой Windows-1251.
Для записи в ini файл использую QSettings, версия Qt 4.4.3
Перепробывал различные варианты, но всегда получаю либо "???????" либо что-то вида
"\x34\x12\x43". Причем в одном из варантов записанная строка "\x34\x53\x23"
представляет собой коды кириллической строки в кодировке Windows-1251.
Английский текст записывается без проблем.
Кроме того, что интересно, если в ini файле есть кириллические строки, но я их не трогаю,
а изменяю другое поле, то они тоже перекодировываються в виде "\x34\x54".
Код:
Код:
QTextCodec *codec = QTextCodec::codecForName( "Windows-1251" );
QLineEdit* ModemName = new QLineEdit;
QSettings* ModemSet = new QSettings("config.txt", QSettings::IniFormat);
ModemName->setText(codec->toUnicode(ModemSet->value("ModemName", "").toByteArray())); //нормально считывает
/.../
ModemSet->setValue("ModemName", ModemName->text().toLocal8Bit().data()); //вот тут загвоздка

Код:
ModemSet->setValue("ModemName", codec->fromUnicode( ModemName->text() ); 


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 16:10
а локаль какая?
Системная стоит Windows-1251. Исходники тоже в ней.
В данный момент пишу программу под Windows XP SP3, Qt 4.4.3 + mingw (gcc 3.4.5).
Для tr установлен кодек по умолчанию windows-1251.
Еще пробывал передавать дополнительно параметр LANG=ru_RU.windows-1251, но это не помогло.


Название: Re: Проблема с записью в ini файл
Отправлено: spirit от Декабрь 01, 2008, 16:15
вот тут была такая же проблема

http://www.prog.org.ru/index.php?topic=7866.msg40748#msg40748
 (http://www.prog.org.ru/index.php?topic=7866.msg40748#msg40748)
применение cp-1252 помогло.


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 16:16
Код:
ModemSet->setValue("ModemName", codec->fromUnicode( ModemName->text() ); 

Все равно получаю свою шифровку "\x34\x53\x23".
Хотя, как я уже отмечал, записанные коды это кириллическая кодировка.


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 16:21
вот тут была такая же проблема

http://www.prog.org.ru/index.php?topic=7866.msg40748#msg40748
 (http://www.prog.org.ru/index.php?topic=7866.msg40748#msg40748)
применение cp-1252 помогло.

Да я читал ту тему. К сожалению в моем случае применение cp-1252 приводит к появлению "?????".
Хотя у меня и записывается в ini файл шифровка, но читается оттуда она программой нормально.
Проблема в том, что данный ini файл будет использоваться еще одной программой, а вот у нее возникнут проблемы.


Название: Re: Проблема с записью в ini файл
Отправлено: spirit от Декабрь 01, 2008, 16:32
а локаль точно русская, у меня просто анлийская и тест знаки вопроса валит, но должно работать.
сделайте тест, установите кодек для setCodecForCStrings cp-1251 и попробуйте в лоб записать русские символы в файл, типа такого
Код
C++ (Qt)
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("cp-1251"));
QSettings settings("test.ini", QSettings::IniFormat);
settings.setValue("str1", "абвгд....");
settings.setValue("str2", "эюя...");
 


Название: Re: Проблема с записью в ini файл
Отправлено: BRE от Декабрь 01, 2008, 16:35
Хотя у меня и записывается в ini файл шифровка, но читается оттуда она программой нормально.
Какой шифровкой? Ты получаешь строку названия модема из элемента управления? Если да, то она передаеться в unicode. Строка в ini файле должна быть в кодировке CP-1251. Используем соответствующий кодек.

Запись в файл:
Код:
QTextCodec *codec = QTextCodec::codecForName( "Windows-1251" );
QSettings *ModemSet = new QSettings("config.txt", QSettings::IniFormat);
QString modemName = editModemName->text();
ModemSet->setValue( "ModemName", codec->fromUnicode( modemName ) );

Чтение из файла:
Код:
QTextCodec *codec = QTextCodec::codecForName( "Windows-1251" );
QSettings *ModemSet = new QSettings("config.txt", QSettings::IniFormat);
QString modemName = codec->toUnicode( ModemSet->value( "ModemName", "" ).toByteArray() );
editModemName->setText( modemName );

Если ты хочешь записать строку названия модема как константу и исходники программы у тебя в CP-1251, то записываешь не используя кодек:
Код:
QSettings *ModemSet = new QSettings("config.txt", QSettings::IniFormat);
ModemSet->setValue( "ModemName", "СуперМодем" ); // Эта строка и так в CP-1251.


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 17:08
а локаль точно русская, у меня просто анлийская и тест знаки вопроса валит, но должно работать.
сделайте тест, установите кодек для setCodecForCStrings cp-1251 и попробуйте в лоб записать русские символы в файл, типа такого
Код
C++ (Qt)
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("cp-1251"));
QSettings settings("test.ini", QSettings::IniFormat);
settings.setValue("str1", "абвгд....");
settings.setValue("str2", "эюя...");
 

Получается что-то вида:
Код:
str1=\x430\x431\x432\x433\x434....
str2=\x44d\x44e\x44f...

Может действительно что-то с локалью, QLocale::system().name() возвращает "ru_RU".


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 17:20
Какой шифровкой? Ты получаешь строку названия модема из элемента управления? Если да, то она передаеться в unicode. Строка в ini файле должна быть в кодировке CP-1251. Используем соответствующий кодек.

Запись в файл:
Код:
QTextCodec *codec = QTextCodec::codecForName( "Windows-1251" );
QSettings *ModemSet = new QSettings("config.txt", QSettings::IniFormat);
QString modemName = editModemName->text();
ModemSet->setValue( "ModemName", codec->fromUnicode( modemName ) );

Чтение из файла:
Код:
QTextCodec *codec = QTextCodec::codecForName( "Windows-1251" );
QSettings *ModemSet = new QSettings("config.txt", QSettings::IniFormat);
QString modemName = codec->toUnicode( ModemSet->value( "ModemName", "" ) );
editModemName->setText( modemName );

Если ты хочешь записать строку названия модема как константу и исходники программы у тебя в CP-1251, то записываешь не используя кодек:
Код:
QSettings *ModemSet = new QSettings("config.txt", QSettings::IniFormat);
ModemSet->setValue( "ModemName", "СуперМодем" ); // Эта строка и так в CP-1251.

Под шифровкой я подразумеваю, что записываются не символы кириллицы, а их цифровое представление вида "\x44". С записью и чтением файла, которые приведены я экспериментировал, но ничего не добился.
Под отладкой видно, что непосредственно до записи в кишках QSettings уже доходит что-то вида windows-1251 интерпретированная как Unicode, может где-то в недрах QVariant происходит необратимые изменения или какие-либо глобальные параметры влияют.


Название: Re: Проблема с записью в ini файл
Отправлено: BRE от Декабрь 01, 2008, 17:21
а локаль точно русская, у меня просто анлийская и тест знаки вопроса валит, но должно работать.
сделайте тест, установите кодек для setCodecForCStrings cp-1251 и попробуйте в лоб записать русские символы в файл, типа такого
Код
C++ (Qt)
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("cp-1251"));
QSettings settings("test.ini", QSettings::IniFormat);
settings.setValue("str1", "абвгд....");
settings.setValue("str2", "эюя...");
 

Получается что-то вида:
Код:
str1=\x430\x431\x432\x433\x434....
str2=\x44d\x44e\x44f...

Может действительно что-то с локалью, QLocale::system().name() возвращает "ru_RU".
Попробуй тотже код, только не делай:
Код:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("cp-1251"));


Название: Re: Проблема с записью в ini файл
Отправлено: BRE от Декабрь 01, 2008, 17:24
может где-то в недрах QVariant происходит необратимые изменения или какие-либо глобальные параметры влияют.
Необратимые изменения это (цитата из Assistant):
Цитировать
QVariant::QVariant ( const char * val )
Constructs a new variant with a string value of val. The variant creates a deep copy of val, using the encoding set by QTextCodec::setCodecForCStrings().


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 17:35
Получается что-то вида:
Код:
str1=\x430\x431\x432\x433\x434....
str2=\x44d\x44e\x44f...
Попробуй тотже код, только не делай:
Код:
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("cp-1251"));

Теперь выходит
Код:
str1=\xe0\xe1\xe2\xe3\xe4....
str2=\xfd\xfe\xff...

Т.е. коды кириллических символов (http://ru.wikipedia.org/wiki/Windows-1251) кодировки Windows-1251


Название: Re: Проблема с записью в ini файл
Отправлено: BRE от Декабрь 01, 2008, 17:42
Т.е. место строки "абвгд....", в файл записывается в виде "xe0\xe1\xe2\xe3\xe4...."?


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 17:55
Т.е. место строки "абвгд....", в файл записывается в виде "xe0\xe1\xe2\xe3\xe4...."?

Да  :(


Название: Re: Проблема с записью в ini файл
Отправлено: BRE от Декабрь 01, 2008, 18:03
Т.е. место строки "абвгд....", в файл записывается в виде "xe0\xe1\xe2\xe3\xe4...."?

Да  :(
Попробовал у себя, такая же фигня.  ???  ::)
Буду пробовать дальше...


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 18:30
Т.е. место строки "абвгд....", в файл записывается в виде "xe0\xe1\xe2\xe3\xe4...."?
Да  :(
Попробовал у себя, такая же фигня.  ???  ::)
Буду пробовать дальше...

Попробовал на другой машине с Qt 4.3.3 собранной вручную с опцией static, то же не работает.
На линукс с Qt 4.4.4-snapshot 20.11.08 та же ситуация  :(


Название: Re: Проблема с записью в ini файл
Отправлено: BRE от Декабрь 01, 2008, 18:39
Т.е. место строки "абвгд....", в файл записывается в виде "xe0\xe1\xe2\xe3\xe4...."?
Да  :(
Попробовал у себя, такая же фигня.  ???  ::)
Буду пробовать дальше...

Попробовал на другой машине с Qt 4.3.3 собранной вручную с опцией static, то же не работает.
На линукс с Qt 4.4.4-snapshot 20.11.08 та же ситуация  :(
Залез в исходники, похоже дальше будет так, т.е. все строки будут сохраняться Escape-последовательностями.

Как вариант остается использование QSettings::registerFormat, и писать-читать как нужно.


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 18:52
Залез в исходники, похоже дальше будет так, т.е. все строки будут сохраняться Escape-последовательностями.
Как вариант остается использование QSettings::registerFormat, и писать-читать как нужно.
Жаль, если это так, не хотелось бы заморачиваться с написанием своих ReadFunc и WriteFunc.
Можно ли это считать багом Qt? Или это просто такая особоя фича?


Название: Re: Проблема с записью в ini файл
Отправлено: BRE от Декабрь 01, 2008, 19:08
Можно ли это считать багом Qt? Или это просто такая особоя фича?
Фича, я думаю, так как сделанно специально.
Лень скачивать старые исходники Qt, как нибудь нужно глянуть. Сам смотрел 4.4.3.


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 01, 2008, 21:07
В свежем снапшоте (http://doc.trolltech.com/main-snapshot/qsettings.html#setIniCodec) появились удивительные функции:
Код:
void QSettings::setIniCodec ( QTextCodec * codec )
и
void QSettings::setIniCodec ( const char * codecName )

Цитирую:
Цитировать
Sets the codec for accessing INI files (including .conf files on Unix) to codec. The codec is used for decoding any data that is read from the INI file, and for encoding any data that is written to the file. By default, no codec is used, and non-ASCII characters are encoded using standard INI escape sequences.

Надо посмотреть случилось чудо или нет  :)


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 02, 2008, 16:21
Надо посмотреть случилось чудо или нет  :)

Чудо свершилось, проблема решена.  :)


Название: Re: Проблема с записью в ini файл
Отправлено: spirit от Декабрь 02, 2008, 19:08
и в чем был косяк?


Название: Re: Проблема с записью в ini файл
Отправлено: pastor от Декабрь 02, 2008, 19:23
и в чем был косяк?

Очевидно prefetch заюзал QSettings::setIniCodec (Qt 4.5) ;)


Название: Re: Проблема с записью в ini файл
Отправлено: spirit от Декабрь 02, 2008, 19:24
аааа  ;D


Название: Re: Проблема с записью в ini файл
Отправлено: prefetch от Декабрь 02, 2008, 23:02
и в чем был косяк?
Очевидно prefetch заюзал QSettings::setIniCodec (Qt 4.5) ;)

Так точно, из снапшота от 1 декабря сего года.
Кроме того, теперь и читать из такого файла стало намного проще.
К примеру:
Код:
QSettings settings("test.ini", QSettings::IniFormat);	
settings.setIniCodec("windows-1251");
QString str;
settings.setValue("str1", "абвгд");
str = settings.value("str1", "");
settings.setValue("str2", str);
QMessageBox::warning(NULL, "hello", str);


Название: Re: Проблема с записью в ini файл
Отправлено: Alexkvs от Июль 05, 2009, 19:18
Цитировать
Так точно, из снапшота от 1 декабря сего года.
Кроме того, теперь и читать из такого файла стало намного проще.
К примеру:
Код:
QSettings settings("test.ini", QSettings::IniFormat);	
settings.setIniCodec("windows-1251");
QString str;
settings.setValue("str1", "абвгд");
str = settings.value("str1", "");
settings.setValue("str2", str);
QMessageBox::warning(NULL, "hello", str);

У меня что-то такая конструкция не работает (всё равно эскейп последовательностями пишет символы). Использую Qt 4.5.0 commercial.


Название: Re: Проблема с записью в ini файл
Отправлено: break от Июль 05, 2009, 20:45
Цитировать
Жаль, если это так, не хотелось бы заморачиваться с написанием своих ReadFunc и WriteFunc.

Сталкивался с описанной здесь проблемой и с некоторыми другими при работе с INI - например абсолючно не читаемое для человека представление таблицы или вложенной таблицы. Написал свои ReadFunc и WriteFunc для XML файлов. Теперь все настройки храню в XML. Интерфейс использования естественно не изменился, уже в какой-то теме выкладывал если нужно выложу здесь еще раз.


Название: Re: Проблема с записью в ini файл
Отправлено: Alexkvs от Июль 06, 2009, 19:23
Цитировать
Жаль, если это так, не хотелось бы заморачиваться с написанием своих ReadFunc и WriteFunc.
... если нужно выложу здесь еще раз.
Выложите если можно, многим думаю пригодится.


Название: Re: Проблема с записью в ini файл
Отправлено: break от Июль 06, 2009, 19:51
выкладываю:
H файл: XmlSettings.h
Код
C++ (Qt)
 
#ifndef __XML_SETTINGS_H__
#define __XML_SETTINGS_H__
 
#include <QString>
#include <QSettings>
 
class QDomDocument;
class QDomElement;
class QIODevice;
 
class CXmlSettings
{
QSettings * m_pSettings;
public:
CXmlSettings( QString fname = "" );
~CXmlSettings();
 
inline QSettings& settings() const { return * m_pSettings; }
 
// при использование CXmlSettings::value конфиг создастся автоматически, если его не было
QVariant value( const QString & key, const QVariant & defaultValue = QVariant() );
void setValue ( const QString & key, const QVariant & value );
 
private:
static bool readXmlFile(QIODevice &device, QSettings::SettingsMap &map);
static bool writeXmlFile(QIODevice &device, const QSettings::SettingsMap &map);
static void processWriteKey( QDomDocument& doc, QDomElement& domElement, QString key, const QVariant& value );
static void processReadKey( QString key, QSettings::SettingsMap &map, QDomElement& domElement );
};
 
#endif // __XML_SETTINGS_H__
 
 

CPP файл: XmlSettings.cpp
Код
C++ (Qt)
 
#include "XmlSettings.h"
#include "FileUtils.h"
#include <QDomDocument>
#include <QDebug>
 
CXmlSettings::CXmlSettings( QString fname )
{
if ( fname.isEmpty() )
fname = settingsPath() + appBaseName() + ".xml";
 
static const QSettings::Format XmlFormat = QSettings::registerFormat("xml", readXmlFile, writeXmlFile);
 
m_pSettings = new QSettings( fname, XmlFormat );
}
 
CXmlSettings::~CXmlSettings()
{
delete m_pSettings;
}
 
QVariant CXmlSettings::value( const QString & key, const QVariant & defaultValue )
{
if ( !settings().contains( key ) )
settings().setValue( key, defaultValue );
return settings().value( key );
}
void CXmlSettings::setValue ( const QString & key, const QVariant & value )
{
settings().setValue( key, value );
}
 
bool CXmlSettings::readXmlFile(QIODevice &device, QSettings::SettingsMap &map)
{
qDebug() << "-----readXmlSettings------";
 
QDomDocument doc("");
if ( !doc.setContent( &device ) ) return false;
 
QDomElement root = doc.documentElement();
 
processReadKey( "", map, root );
 
/*
// Для теста использовалось при отладке
QMap<QString, QVariant>::const_iterator i = map.constBegin();
while (i != map.constEnd()) {
qDebug() << i.key() << ": " << i.value() << endl;
++i;
}
*/

return true;
}
 
bool CXmlSettings::writeXmlFile(QIODevice &device, const QSettings::SettingsMap &map)
{
qDebug() << "-----writeXmlSettings-----";
 
/*
// Для теста использовалось при отладке
QMap<QString, QVariant>::const_iterator i = map.constBegin();
while (i != map.constEnd()) {
qDebug() << i.key() << ": " << i.value() << endl;
++i;
}
*/

 
QDomDocument doc("");
QDomElement root = doc.createElement("Main");
doc.appendChild(root);
 
QMapIterator<QString, QVariant> i(map);
while ( i.hasNext() )
{
i.next();
 
QString  sKey = i.key();
QVariant value = i.value();
processWriteKey( doc, root, sKey, i.value() );
};
 
QDomNode xmlNode = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
doc.insertBefore(xmlNode, doc.firstChild());
 
QTextStream out( &device );
doc.save(out, 4);
 
return true;
}
 
void CXmlSettings::processWriteKey( QDomDocument& doc, QDomElement& domElement, QString key, const QVariant& value )
{
int slashPos = key.indexOf( '/' );
 
// переданный ключ является параметром
if ( slashPos < 0 )
{
// не пишем в конфиг параметр size (является ограничением - нельзя исп. пар-тр с таким именем)
if ( key == "size" ) return;
domElement.setAttribute( key, value.toString() );
return;
};
 
// получение имени группы соответствующей xml ноде
QString groupName = key.left( slashPos );
// если в качестве имени использован числовой параметр - это табличная строка, преобразуем ее в row_?
if ( groupName.toInt() )
{
groupName = "row_" + groupName;
domElement.toElement().setAttribute("table", "1");
};
 
// поиск/создание ноды соответствующей ключу
QDomElement groupElement;
 
QDomNode findedGroupNode = domElement.namedItem( groupName );
 
if ( findedGroupNode.isNull() )
{
groupElement = doc.createElement( groupName );
domElement.appendChild( groupElement );
}
else
groupElement = findedGroupNode.toElement();
 
// готовим обрезанную часть ключа
key = key.right( key.size() - slashPos - 1 );
 
// продолжение обработки (создание/поиск групп) - пока не найдется параметр
processWriteKey( doc, groupElement, key, value );
}
 
void CXmlSettings::processReadKey( QString key, QSettings::SettingsMap &map, QDomElement& domElement )
{
 
QDomNamedNodeMap namedNodeMap = domElement.attributes();
 
// Добавление всех атрибутов элемента в качестве значений
for (int i = 0; i < namedNodeMap.count(); ++i)
{
QString name = namedNodeMap.item( i ).toAttr().name();
QString value = namedNodeMap.item( i ).toAttr().value();
map.insert( key + name, value );
};
 
QDomNodeList nlChild = domElement.childNodes();
 
// если узел является таблицей - то все дети строки
bool isTable = domElement.attribute("table", "0").toInt();
// создаем доп. элемент size равный числу детей (необходим для QSettings - beginArray)
if ( isTable )
map.insert( key + "size", nlChild.count() );
 
// проход по всем детям
for (int i = 0; i < nlChild.count(); ++i)
{
QString childName = nlChild.item(i).toElement().tagName();
if ( childName.contains("row_") )
childName = childName.right( childName.size() - 4 );
 
QString subKey = key + childName + "/";
QDomElement subElement = nlChild.item(i).toElement();
processReadKey( subKey, map, subElement );
};
}
 
 

Функции settingsPath()  и  appBaseName() - у меня реализованы в моем FileUtils - возвращают дефолтный путь к папке с настройками и базовое имя программы (без расширения)

Также продублировал во вложении.

Пользоваться можно через обращение к settings() - всеми возможностями самого QSettings - группы, массивы и т.д. Ограничение - нельзя использовать атрибут с именем "table" - он используется для определения таблицы (массива) при чтение настроек. Если пользоваться через прямые методы value, CXmlSettings - то в случае если значения в конфиге не было - оно создасться и заполниться значением по умолчанию (сделал чтобы программа сама создавала свой конфиг если был утерян...).


Название: Re: Проблема с записью в ini файл
Отправлено: ритт от Август 11, 2009, 19:01
break, продублируй, пожалуйста, последний пост в "доп.компонентах". и хотелось бы файлик лицензии в папке с исходниками...