char arr[] = new char[]{'a','b','c','d'};
int N = arr.length;
for (int mask = 0; mask < (1 << N); mask++) {//перебор масок
for (int j = 0; j < N; j++) {//перебор индексов массива
if((mask & (1 << j)) != 0){//поиск индекса в маске
System.out.print(arr[j] + " ");//вывод элемента
}
}
System.out.println();//перевод строки для вывод следующего подмножества
}
А теперь пояснения.
1 << N - побитовый сдвиг единицы на N позиций влево.
В двоичном виде единица выглядит так 00000001. В нашем случае N = 4.
После сдвига на 4 позиции влево появится новое число, двоичный вид которого 00010000. Теперь, если вычесть из этого числа единицу получим: 00001111. Если опустить ведущие нули, то получим 1111. Четыре единицы - это состояние, когда в подмножество попадают все элементы, 0000 - пустое множество, 1010 - подмножество состоит из первого и третьего элемента множества (нумерация с нуля, отсчет ведется справа) и т.д.
Т.е. мы перебираем числа, двоичное представление которых от 0000 до 1111.
После того как мы получили маску (mask), необходимо узнать в каких позициях стоят единицы. Проверять будем следующим образом.
Например, на каком-то шаге цикла программы маска равна 1100, мы перебираем все позиции путем сдвига единицы: 0001, 0010, 0100 и 1000. Для проверки воспользуемся операцией побитового И (если в одинаковых позициях стоят единицы, то результат единица, иначе ноль). В Java обозначается как &
В нашем случае:
1100 & 0001 = 0000
1100 & 0010 = 0000
1100 & 0100 = 0100
1100 & 1000 = 1000
Т.е. если в итоге получилось число отличное от нуля, то включаем элемент в множество.
0100 - включаем в множество элемент с индексом 2
1000 - включаем в множество элемент с индексом 3
Нумерация с нуля и справа.
Результат работы программы (первая строка пустая - это пустое множество):
a b a b c a c b c a b c d a d b d a b d c d a c d b c d a b c dВ данной реализации мы хранили маски в типе int. (1 << 30) равно 1 073 741 824 Безопасное значение N для типа int: 30. Свыше 30 будет переполнение переменных типа int, поэтому нужно использовать long. При N > 30 перебор будет ощутимо все медленнее и медленнее. Если будете делать реализацию с long, то при сдвиге единицы нужно писать так:
1L << j
Т.е. нужно к единице приписать L, и тогда результат операции будет long, в противном случае будет int с переполнением.
String text = "keeshh";
String result = text.replaceAll("([a-z])\\1", "");
System.out.println(result);//"ks"
replaceAll - функция для замены всех совпадений
[a-z] - выражение говорит о том, что на этом месте должен быть символ в диапазоне от a до z.
() - круглые скобки означают группировку, сослаться на которую можно по номеру, причем нумерация начинается с единицы
\1 - указывает на то, что в этом месте должен быть такой же текст, как в группировке под номером 1. Обратный слэш "\" необходимо экранировать, поэтому в выражении два слэша "\\"
String text = "keeshhs";
int len;
do{
len = text.length();//сохраняем длину строки
text = text.replaceAll("([a-z])\\1", "");
}while(len != text.length());//сравниваем новую длину строки с сохраненной длиной
System.out.println(text);//"k"
String text = "keeeeshh"; text = text.replaceAll("([a-z])\\1+", ""); System.out.println(text);//"ks"Как вы, наверное, заметили после единички добавился символ +. Плюс в регулярных означает, что предшествующий плюсу символ или группировка должна встретиться от одного раза и более. Можно также включить этот код в цикл, чтобы снова заменить появившиеся повторения.
String text = "kkeesshh";
text = text.replaceAll("([a-z])\\1", "$1");
System.out.println(text);//"kesh"
Для замены мы использовали выражение $1, которое говорит о том, что текст, соответствующий регулярному выражению, необходимо заменить на текст, соответствующий группировке с номером 1. В нашем случае группировка с номером - это текст, соответствующий шаблону [a-z].
Можно переписать код, используя группу с номером 2 для замены:
String text = "kkeesshh";
text = text.replaceAll("([a-z])(\\1)", "$2");
System.out.println(text);//"kesh"
Вторая группа соответствует шаблону \\1
String text = "keeeeessshh";
text = text.replaceAll("([a-z])\\1+", "$1");
System.out.println(text);//"kesh"
org.apache.http.NoHttpResponseException: The target server failed to respondПотом локализовал ошибку. Вот фрагмент проблемного кода:
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);//тайм-аут подключения
HttpConnectionParams.setSoTimeout(httpParams, 10000);//тайм-аут сокета
HttpClient httpClient = new DefaultHttpClient(httpParams);
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = httpClient.execute(httpGet);//тут выбрасывается Exception
Попробовал переписать код так (без HTTP-параметров).:
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = httpClient.execute(httpGet);
Статистика в приложении начала работать. Т.е. дело оказалось в передаваемых параметрах. Мне кажется, провайдер хостинга что-то обновил, и теперь сервер перестал понимать передаваемые запросы.
Погуглив, нашел другой способ передачи параметров, способ - работает!
HttpClient httpClient = new DefaultHttpClient();
httpClient.getParams().setParameter(HttpConnectionParams.CONNECTION_TIMEOUT, 5000);
httpClient.getParams().setParameter(HttpConnectionParams.SO_TIMEOUT, 10000);
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = httpClient.execute(httpGet);
Как видно из приведенного выше кода:
cls
set rar="C:\Program Files\WinRAR\Rar.exe"
set source_dir="D:\Projects\ProjectX"
set backup_dir="D:\Backup\ProjectX\projectx_"
%rar% a -r -ep1 -agYYYY-MM-DD[HHMM] %backup_dir% %source_dir%
cls - необязательная команда, которая очищает консоль
";
}
//вывод содержимого объекта на экран
function debugObj($obj){
echo "<";
echo "pre style='color:green'>";
print_r($obj);
echo "";
echo "pre>";
}
//получение заголовка письма в виде объекта
function getHeader($mbox,$uid){
return imap_rfc822_parse_headers(getHeaderRaw($mbox,$uid));
}
//получение заголовка письма
function getHeaderRaw($mbox,$uid){
return imap_fetchbody($mbox, $uid, '0', FT_UID);
}
//раскодировка заголовка
function getDecodedHeader($text){
//imap_mime_header_decode - декодирует элементы MIME-шапки в виде массива
//У каждого элемента указана кодировка(charset) и сам текст(text)
$elements = imap_mime_header_decode($text);
$ret = "";
//перебираем элементы
for ($i=0; $icharset;//кодировка
$text = $elements[$i]->text;//закодированный текст
if($charset == 'default'){
//если элемент не кодирован, то значение кодировки default
$ret .= $text;
}else{
//приводим всё кодировке к UTF-8
$ret .= iconv($charset,"UTF-8",$text);
}
}
return $ret;
}
//получение содержимого письма в виде простого текста
function getTextBody($imap,$uid){
return getPart($imap, $uid, "TEXT/PLAIN");
}
//получение содержимого письма в виде формате html
function getHtmlBody($imap,$uid){
return getPart($imap, $uid, "TEXT/HTML");
}
//получение части письма
function getPart($imap, $uid, $mimetype) {
//получение структуры письма
$structure = imap_fetchstructure($imap, $uid, FT_UID);
if ($structure) {
if ($mimetype == getMimeType($structure)) {
$partNumber = 1;
//imap_fetchbody - извлекает определённый раздел тела сообщения
$text = imap_fetchbody($imap, $uid, $partNumber, FT_UID);
$charset = $structure->parameters[0]->value;//кодировка символов
//0 - 7BIT; 1 - 8BIT; 2 - BINARY; 3 - BASE64; 4 - QUOTED-PRINTABLE; 5 - OTHER
switch ($structure->encoding) {
case 3:
//imap_base64 - декодирует BASE64-кодированный текст
$text = imap_base64($text);
break;
case 4:
//imap_qprint - конвертирует закавыченную строку в 8-битную строку
$text = imap_qprint($text);
break;
}
if($mimetype == 'TEXT/PLAIN'){
$text = iconv($charset,"UTF-8",$text);
}
if($mimetype == 'TEXT/HTML'){
$text = iconv($charset,"UTF-8",$text);
}
return $text;
}
}
return false;
}
//MIME-тип передается числом, а подтип - текстом.
//Функция приводит все в текстовый вид.
//Например: если type = 0 и subtype = "PLAIN",
//то функция вернет "TEXT/PLAIN".
//TEXT - 0, MULTIPART - 1, .. , APPLICATION - 3 и т.д.
function getMimeType($structure) {
$primaryMimetype = array("TEXT", "MULTIPART", "MESSAGE", "APPLICATION",
"AUDIO", "IMAGE", "VIDEO", "OTHER");
if ($structure->subtype) {
return $primaryMimetype[(int)$structure->type] . "/" . $structure->subtype;
}
return "TEXT/PLAIN";
}
//перевести текст в дату MySQL
function strToMysqlDate($text){
$unixTimestamp=strtotime($text);
return date("Y-m-d H:i:s", $unixTimestamp);
}
//заполняем ассоциативный массив, где ключом является тип адреса,
//а значение массив адресов
function getAddress($header,$type,&$map){
//проверка существования типа в заголовке
if(property_exists($header,$type)){
$arr = $header->$type;
if(is_array($arr) && count($arr) > 0){
$map[$type] = $arr;
}
}
}
//загрузка вложений
function loadAttaches($mbox,$uid,$message_id){
//получаем структуру сообщения
$struct = imap_fetchstructure($mbox,$uid,FT_UID);
$attachCount = 0;
if(!$struct->parts) return $attachCount;
//перебираем части сообщения
foreach($struct->parts as $number => $part){
//ищем части, у которых ifdisposition равно 1 и disposition равно ATTACHMENT,
//все остальные части игнорируем. Также стоит заметить, что значение поля
//disposition может быть как в верхнем, так и в нижнем регистрах,
//т.е. может быть "attachment" и "ATTACHMENT". Поэтому в коде всё приведено
//к верхнему регистру
if(!$part->ifdisposition
|| strtoupper($part->disposition) != "ATTACHMENT")continue;
//получаем название файла
$filename = getDecodedHeader($part->dparameters[0]->value);
//получаем содержимое файла в закодированном виде
$text = imap_fetchbody($mbox, $uid, $number + 1, FT_UID);
//декодирование содержимого файла
switch ($part->encoding) {
case 3:
$text = imap_base64($text);
break;
case 4:
$text = imap_qprint($text);
break;
}
//оригинальное название файла будем сохранять в базе данных.
//Разные письма могут иметь вложения с одинаковыми названиями,
//поэтому в файловой системе будем сохранять файла с уникальным именем,
//сохранив при этом расширение файла
$file_path = getStoreDirectory() . getUid() . getFileExtension($filename);
file_put_contents($file_path,$text);
$content_type = getMimeType($part);//MIME-тип файла
$filesize = strlen($text);//размер файла
//записываем информацию о файле в базу данных. Напомню, что в
//базу сохраняется не сам файл, а относительный путь к файлу
$sql = "INSERT INTO attachments(message_id,file_name,mime_type,
file_size,location)" .
"VALUES('$message_id',
'" . mysql_real_escape_string($filename) . "',
'" . mysql_real_escape_string($content_type) . "',
$filesize,
'" . mysql_real_escape_string($file_path) . "')";
$res_ins = mysql_query($sql) or die(mysql_error());
$attachCount++;
}
return $attachCount;
}
//Функция для получения пути к директории, где будут храниться файлы.
//Файлы будут сохраняться в поддиректории, созданной по
//текущей дате. Например, 2014-07-31. Это позволит
//не держать файлы в одной директории. Много файлом в
//одной директории замедляет чтение директории
function getStoreDirectory(){
global $FILES;
$date_folder = "$FILES/" . date('Y-m-d') . "/";
if(!file_exists($date_folder)) mkdir($date_folder);
return $date_folder;
}
//генерация уникального идентификатора
function getUid(){
if (function_exists('com_create_guid')){
return str_replace("}", "", str_replace("{", "", com_create_guid()));
} else {
mt_srand((double)microtime()*10000);//optional for php 4.2.0 and up.
$charid = strtoupper(md5(uniqid(rand(), true)));
$hyphen = chr(45);// "-"
$uuid =
substr($charid, 0, 8).$hyphen
.substr($charid, 8, 4).$hyphen
.substr($charid,12, 4).$hyphen
.substr($charid,16, 4).$hyphen
.substr($charid,20,12);
}
return strtolower($uuid);
}
//получаем расширение файла
function getFileExtension($filename){
$arr = explode(".",$filename);
return count($arr) > 1 ? "." . end($arr) : "";
}
?>
Файл для подключения к базе данных (connect.php):
Исходный код можно скачать здесь
subject);//тема письма
$headerDate = strToMysqlDate($header->date);//дата письма
$body_text = getTextBody($mail,$message_uid);//содержимое письма в виде простого текста
$body_html = getHtmlBody($mail,$message_uid);//содержимое письма в формате html
//получение адресов из заголовка письма
$address_map = array();
$address_types = array('to','from','reply_to','sender','cc','bcc');
foreach($address_types as $address_type){
getAddress($header,$address_type,$address_map);
}
foreach($address_map as $key => $arr){
foreach($arr as $obj){
$type = $key;
$address = "$obj->mailbox@$obj->host";//склеиваем email
$sql = "INSERT INTO addresses(message_id,type,email)
VALUES($message_id,
'" . mysql_real_escape_string($type) . "',
'" . mysql_real_escape_string($address) . "')";
mysql_query($sql) or wr(mysql_error());
}
}
//считываем вложения и получаем кол-во вложений,
//которое записываем в базу данных
$attachCount = loadAttaches($mail,$message_uid,$message_id);
$sql = "UPDATE messages
SET subject = '" . mysql_real_escape_string($subject) . "',
body_text = '" . mysql_real_escape_string($body_text) . "',
body_html = '" . mysql_real_escape_string($body_html) . "',
header = '" . mysql_real_escape_string($headerRaw) . "',
message_date = '" . mysql_real_escape_string($headerDate) . "',
attachment_count = $attachCount,
modify_date = now(),
is_ready = true
WHERE id = $message_id";
mysql_query($sql) or wr(mysql_error());
mysql_query("COMMIT");
}
}
}
?>
CREATE DATABASE mail_collector DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Создадим таблицу mailboxes, которая будет хранить необходимую информацию для подключения к почтовому ящику.
CREATE TABLE mailboxes
(
id int PRIMARY KEY NOT NULL AUTO_INCREMENT,
email varchar(50) NOT NULL,
password varchar(50) NOT NULL,
host varchar(50) NOT NULL,
port varchar(50) NOT NULL,
is_ssl bit NOT NULL,
is_deleted bit NOT NULL,
last_message_uid int NOT NULL
);
Назначение полей:
email - почтовый ящик
password - пароль от ящика
host - адрес почтового сервера. Например, imap.gmail.com или 173.194.71.108
port - порт, по которому работает почтовый сервер
is_ssl - если флаг установлен, то подключение будет по SSL
is_deleted - если флаг установлен, то почтовый ящик считается удаленным и не участвует в сборке писем
last_message_uid - поле используется для хранения UID последнего считанного сообщения
Заполним таблицу mailboxes данными трех почтовых ящиков, созданных на разных почтовых серверах: gmail, yandex и mail.ru. В качестве примера к почтовым ящикам на gmail и yandex будем подключаться через SSL, а на mail.ru - без SSL.
INSERT INTO mailboxes(email,password,host,port,is_ssl,is_deleted) VALUES
('mail.collector.kz@gmail.com','mail.collector','imap.gmail.com','993',1,0,0),
('mail.collector.kz@mail.ru','mail.collector','imap.mail.ru','143',0,0,0),
('mail.collector.kz@yandex.ru','mail.collector','imap.yandex.ru','993',1,0,0)
Таблица сообщений
Создадим таблицу messages для хранения сообщений:
CREATE TABLE messages
(
id int PRIMARY KEY NOT NULL AUTO_INCREMENT,
mailbox_id int NOT NULL,
uid int NOT NULL,
subject varchar(255),
body_text text,
body_html text,
attachment_count int,
header text,
message_date datetime,
create_date datetime NOT NULL,
modify_date datetime,
is_ready bit NOT NULL
);
Так же создадим внешний ключ на таблицу mailboxes:
ALTER TABLE messages ADD CONSTRAINT fk_messages_user_id
FOREIGN KEY (mailbox_id) REFERENCES mailboxes(id);
Назначение полей:
mailbox_id - ссылка на почтовый ящик, к которому относится письмо
uid - уникальный номер письма в почтовом ящике
subject - тема письма
body_text и body_html - письмо храниться на сервере в виде обычного текста и html-версии
attachment_count - кол-во вложений письма
header - технический заголовок письма
message_date - дата письма
create_date - дата создания записи
modify_date - дата изменения записи
is_ready - письмо полностью загружено
Таблица адресов
Создадим таблицу addresses для хранения адресов: адрес отправителя, адреса получателей, адреса получателей скрытой копии и т.д.
CREATE TABLE addresses
(
id int PRIMARY KEY NOT NULL AUTO_INCREMENT,
message_id int NOT NULL,
type varchar(10) NOT NULL,
email varchar(50) NOT NULL
);
И создадим внешний ключ для связи с таблицей messages:
ALTER TABLE addresses ADD CONSTRAINT fk_addresses_message_id
FOREIGN KEY (message_id) REFERENCES messages(id);
Назначение полей:
message_id - ссылка на сообщение, к которому относится данный адрес
type - тип адресата: "from" (отправитель), "to"(получатель), "cc" (скрытая копия) и т.д.
email - адрес электронной почты
Таблица вложений
Создадим таблицу attachments для хранения информации о файлах прикрепленных к письму:
CREATE TABLE attachments
(
id int PRIMARY KEY NOT NULL AUTO_INCREMENT,
message_id int NOT NULL,
file_name varchar(255) NOT NULL,
mime_type varchar(255) NOT NULL,
file_size int NOT NULL,
location varchar(255) NOT NULL
);
Создадим внешний ключ для связи с таблицей messages:
ALTER TABLE attachments ADD CONSTRAINT fk_attachments_message_id
FOREIGN KEY (message_id) REFERENCES messages(id);
Назначение полей:
message_id - привязка к конкретному сообщению
file_name - имя файла
mime_type - тип данных: "application/zip", "application/pdf", "image/png" и т.д.
file_size - размер файла в байтах
location - расположение файла
СКРИПТЫ
Описание работы: первый скрипт подключается к почтовому серверу, записывает в базу данных UID-ы новых писем, а второй скрипт, который будет рассмотрен позднее, по UID писем закачивает содержимое писем с вложениями и другой информацией.
Скрипт для чтения новых писем (reader.php)
uid;
wr("add message $message_uid");
//создаем запись в таблице messages,
//тем самым поставив сообщение в очередь на загрузку
$sql = "INSERT INTO messages(mailbox_id,uid,create_date,is_ready)
VALUES($mailbox_id,$message_uid,now(),0)";
mysql_query($sql) or die(mysql_error());
}
if($message_uid != - 1){
wr("last message uid = $message_uid");
//если появились новые сообщения,
//то сохраняем UID последнего сообщения
$sql = "UPDATE mailboxes
SET last_message_uid = $message_uid
WHERE id = $mailbox_id";
mysql_query($sql) or die(mysql_error());
}else{
//нет новых сообщений
wr("no new messages");
}
}
mysql_query("COMMIT");//завершаем транзакцию
//закрываем IMAP-поток
imap_close($mail);
}
?>
public String firstUpperCase(String word){
if(word == null || word.isEmpty()) return "";//или return word;
return word.substring(0, 1).toUpperCase() + word.substring(1);
}
word.substring(0, 1) - возвращает первую букву в слове
word.substring(0, 1).toUpperCase() - переводит первую букву в верхний регистр
word.substring(1) - добавляет остальные символы без изменения
Пример использования:
String var = "name";
System.out.println("get" + firstUpperCase(var) + "()");//Выведет: "getName()"