C# распаковка gzip-архива (в .NET Framework 2.0)

Для работы с gzip-архивами есть класс GZipStream из пространства имен System.IO.Compression, доступный в .NET Framework 2.0, однако в MSDN почему-то получилось, как в анекдоте про Вовочку, класс есть, а слова такого нету примера под второй фреймворк нет. В том, который есть, используется отсутствующий метод CopyTo() Пришлось действовать без него:

1. Заводим три потока, собственно GZipStream два FileStream для чтения сжатого и записи распакованного файла.

GZipStream gzip = null;
FileStream readStream = null;
FileStream writeStream = null;

Все дальнейшее лучше делать в try/catch, чтобы отловить возможные ошибки

2. Открываем оригинальный файл на чтение, распакованный на запись:

readStream = new FileStream(originalFile, FileMode.Open);
writeStream = new FileStream(unpackedFile, FileMode.Create);

3. Создаем поток GZipStream, подсовываем ему поток, откуда читать данные, и устанавливаем CompressionMode в Decompress

gzip = new GZipStream(readStream,CompressionMode.Decompress);

А теперь делаем CopyTo, только без самой CopyTo:

1. Заводим переменную с размером буфера, сам буфер, и переменную для хранения фактического количества прочитанных из потока GZipStream байт:

int size = 1024; //размер буфера для обмена между потоками
byte[] unpackbuf = new byte[size]; //буфер
int count = 0; //для хранения фактически прочитанных байт

Чем больше размер буфера, тем быстрее пойдет процесс.

2. Читаем данные кусками размером size из GZipStream и пишем их в поток выходного файла:

//пишем распакованные данные по кускам
do
{
 	count = gzip.Read(unpackbuf, 0, size); //читаем кусками размером size
 	if (count > 0) //если данные есть
 	{                        
 		writeStream.Write(unpackbuf, 0, count); //пишем 
 		//фактически прочитанное кол-во байт
  	}
} while (count > 0);

3. Закрываем потоки:
gzip.Close();
readStream.Close();
writeStream.Close();

И ТЕЛЕМАРКЕТ!

Функция целиком под катом

Получение имени распакованного файла, как в других программах, работающих с gzip

На самом деле, архив формата gzip не хранит исходное имя сжатого файла, и вообще хранит один единственный файл. Однако принято, что распакованный файл носит то же имя, что и имя архива, но без расширения .gz, если оно присутствует. Вот функция, которая «отрезает» расширение:

public string GetUnpackedFilename(string fileName)
{
    FileInfo fi = new FileInfo(fileName);
    string unpackedFile = fileName.Substring(0, fileName.Length -
        fi.Extension.Length);
    return unpackedFile;
}

Код на PasteBin
На Github

C# является ли файл gzip-архивом

Возникла задача работать с данными, которые могут быть сжаты в gzip-архив. Соответственно, необходимо было определить, является ли файл gzip-архивом. Это довольно просто, gzip-архив можно определить по сигнатуре 1f 8b 08 00. Проверку целостности оставим на функцию распаковки, и если вышеуказанная сигнатура обнаружена в начале файла, то будем считать, что перед нами архив gzip.
Вот функция проверки:

public bool IsGZip(string filename)
        {
            byte[] buf = null;
            try
            {
                buf = File.ReadAllBytes(filename);
            }
            catch
            {
                return false;
            }

            if (buf.Length < 4) return false;

            if ((buf[0] == 0x1F) && (buf[1] == 0x8B) &&
                (buf[2] == 0x08) && (buf[3] == 0x00))
                return true;

            return false;
        }

Если файл очень большой, то File.ReadAllBytes не подойдет. Надо будет через StreamReader или FileStream прочесть только первые 4 байта:

public bool IsGZip(string filename)
        {
            int signlen = 4;
            int count = 0;
            byte[] buf = new byte[signlen];
            FileStream readStream = null;
            try
            {
                readStream = new FileStream(filename, FileMode.Open);
                count = readStream.Read(buf, 0, signlen);
            }
            catch
            {
                return false;
            }
            readStream.Close();

            if (count < 4) return false;

            if ((buf[0] == 0x1F) && (buf[1] == 0x8B) &&
                (buf[2] == 0x08) && (buf[3] == 0x00))
                return true;

            return false;
        }

Код на PasteBin
Код на PasteBin (вариант 2)

Источник

C# Про конвертирование строки в Double (или любой другой тип с плавающей запятой)

Напоролся на тривиальный, но неприятный подводный камень. Функция Convert.ToDouble() по умолчанию смотрит на разделитель целой и дробной части, который указан в системных настройках. И если в строке разделитель другой, то генерирует exception, например, если конвертировать число 3.14, а в системных настройках в качестве разделителя указана не . (точка), а , (запятая), то программа вывалится с ошибкой.

Решение простое, с помощью второго параметра функции Convert.ToDouble() задать разделитель целой и дробной части. Вот пример функции-обертки над Convert.ToDouble(), с возможностью указания разделителя:

public static double ConvertToDouble(string Value, string DecimalSeparator)
        {
            NumberFormatInfo format = new NumberFormatInfo();
            format.NumberDecimalSeparator=DecimalSeparator;
            return Convert.ToDouble(Value, format);
        }

На самом деле, с помощью NumberFormatInfo можно задать еще кучу параметров, например, разделитель разрядов, буде такой понадобится.

Код на PasteBin

Источник

MSDN

Весна в Гонконге

Кстати, началась весна, а я случайно обнаружил О-о-очень древнюю запись. Эмо-панк группа с характерным названием «Выхода нет». Трэк «Весна в Гонконге». Доложили, что тема петрозаводская. Не гуглил, не проверял, с авторами не связывался

Прямая ссылка

Слушайте. Текст — охуенный.

Метеошно-компьютерное

Расшифровка полей данных SYNOP и METAR в формате CSV

METAR
SYNOP

Скачать в ZIP

Взято с RP5.RU, и собственно, для работы с архивами погоды оттуда и предназначается. Описание полей выдирал L.S. из кода страничек.
Кстати, не сочтите за рекламу, но, ИМХО, это самый классный погодный сайт во всем интернете, не только в рунете, а во всем вообще! Потому что единственный, где можно взять наблюдения непосредственно с нужной метеостанции. Обычно показывают ближайшую к выбранному населенному пункту, но можно, немного изъебнувшись, найти нужную по WMO ID (кто не знает, это такой уникальный типа «айпишник» конкретной станции). Единственный минус — какая-то автоматизация получения данных, только за бабки, запросами выдрать не получается, сайт заскриптован наглухо, но за бабки дают XML, который можно крутить-вертеть, как хочешь.

N-ART 2018 №1


Крупный вариант по клику

Возобновляем мысленный проект Димы

N-ART

Итак, что такое N-ART, это типа арт-журнал, где типа эксперты выбирают типа произведения искусства для одной цели — опубликовать их на нашем сайте. Критериев отбора всего 2:
1. Нам понравилось
2. Произведение нигде не было опубликовано.

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

Политика редакции. Мы не получаем с публикации денег. Если кто-то из авторов хочет деньги, то он может приписать, что ему нужен донат. Весь донат для автора будет отправлен автору. Мы не берем процентов.
На остальные заебы похуй, весь цифровой контент, попавший к нам распространяется по лицензии Хекса

Сегодня у нас произведение Длиннопанка из города К.

Linux, curl, обнаружение и анализ ошибок HTTP. Улучшаем скрипт.

В первой части я показал, как просто обнаружить ошибки HTTP при работе с curl. Но на самом деле, подход, использованный в скрипте, немного неправильный:

Клиент может не знать все коды состояния, но он обязан отреагировать в соответствии с классом кода. В настоящее время выделено пять классов кодов состояния.

Т.е. объясняя по рабоче-крестьянски, не только код 200 может свидетельствовать об успешном завершении запроса, а другие коды, кроме явных клиентских или серверных ошибок, могут требовать от нас каких-либо действий.

Вообще, коды ответа HTTP разделены на 5 классов:
1xx — информационные
2xx — успешное завершение
3xx — требуется переопределение
4xx — ошибка, допущенная со стороны клиента
5xx — ошибка, допущенная со стороны сервера

Вот и модифицируем наш скрипт в соответствии со стандартом.
В общем, надо из кода ответа HTTP извлечь первую цифру, и, соответственно прореагировать. Привожу часть скрипта, ответственного за более подробную реакцию на код ответа HTTP:

curl -o $SAVEFILE $1 -D $HEADERDUMP >/dev/null 2>/dev/null
EXITCODE=$?
if [ $EXITCODE -ne 0 ]; then
    echo "CURL error $EXITCODE"
else
    HTTPSTATUS=`cat $HEADERDUMP|head -1|awk '{print $2}'` #get HTTP status code
    HTTPSTATUSMESS=`cat $HEADERDUMP|head -1|cut -d '  ' -f 3-
    HTTPSTATUSID=`echo $HTTPSTATUS|cut -c 1` #get first char status code
    
    case "$HTTPSTATUSID" in
	1 ) echo -n "Informational: ";;
	2 ) echo -n "Success: ";;
	3 ) echo -n "Redirection: ";;
	4 ) echo -n "Client Error: ";;
	5 ) echo -n "Server Error: ";;
	* ) echo -n "Unknow status: ";;
    esac
    
    echo "$HTTPSTATUS $HTTPSTATUSMESS"
fi


Жирным шрифтом выделена строка, в которой мы получаем этот самый класс кода возврата.

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

Используемая в процессе анализа утилита cut, в первом случае, позволяет нам вывести в переменную все символы после с третьего пробела.
Перед первым у нас версия протокола http, потом код ошибки, а потом пояснение, которое может содержать несколько пробелов, поэтому awk тут не подходит.
cut так же может работать с текстовыми данными, в которых есть разделители, например пробелы, или же с отдельными символами, как в той строке кода, что выделена жирным — так мы можем извлечь первую цифру из кода ответа.

Дальнейший анализ осуществляется с помощью оператора case

Пример работы

Скрипты

Расширенный анализ кодов состояния HTTP:
На Pastebin
На Github

PHP скрипт для проверки:
На Pastebin
На Github

Дополнительные источники

Краткий справочник по командам curl и wget Копия
Утилита cut
Коды статуса HTTP
Оператор case

Начало

curl в Linux, ошибка 404, обнаружение и использование кодов ответа HTTP

Итак, возникла следующая задача — определить, когда curl получил вменяемые данные от http-сервера, а когда нет. В общем, и самом распространенном случае, задача сводится к тому, чтобы определить, отдал ли нам сервер запрашиваемый файл, или отобразил «страница не найдена».

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

Ясно, что сервер, на наш запрос GET может ответить, как возвратив нам запрашиваемую страницу, так и отправив код, например, 404 (не найдено), и возвратив страницу-заглушку для этого случая. Так вот, на уровне скрипта bash или скрипта php необходимо проанализировать ответ сервера, чтобы не выдать пользователю вместо ожидаемых данных, всяческую лабуду. Далее я рассматриваю исключительно консольную Linux-утилиту curl. Для php, синтаксис, по-моему, отличается.

Есть страница, которую мы хотим получить: http://example.org/page.html, сохранить ее в файл page.html и есть curl, команда будет такая:

curl -o "page.html" http://example.org/page.html

Если страница на месте, то мы получим ее, и ничего анализировать не требуется.
Если же нет, то от большинства web-серверов мы получим страницу-заглушку, например такую, но никакой ошибки при этом.

Как же быть:

Проверяем код ошибки самого curl, если нет связи с сетью, URL неправильный, или задан неподдерживаемый curl протокол, если так, то мы получим соответствующий код возврата, наподобие:

curl: (1) Protocol htt not supported or disabled in libcurl

А вот если адрес правильный, и никакой ошибки в адресе или соединении нет, то код возврата не сработает, нам самим придется думать дальше.

Надо получить код ответа HTTP!

Сделать это можно, если получить отдельно заголовки ответа сервера, и после их проанализировать. Благо, для этого никакого страшного колдунства не требуется, достаточно указать утилите curl опцию -D (т.е. dump) и файл, куда выгружать заголовки.

Покажу это на примере конкретного скрипта:

#!/bin/bash

HEADERDUMP="/tmp/headerdump.txt"
SAVEFILE="/tmp/httpfile"
HTTPSTATUS=""

curl -o $SAVEFILE $1 -D $HEADERDUMP >/dev/null 2>/dev/null
EXITCODE=$?
if [ $EXITCODE -ne 0 ]; then
    echo "CURL error $EXITCODE"
else
    HTTPSTATUS=`cat $HEADERDUMP|head -1|awk '{print $2}'`
    
    if [ "$HTTPSTATUS" == "200" ];then
	echo "OK"
    else
	echo "HTTP error $HTTPSTATUS"
    fi
fi

заводим 3 переменные

HEADERDUMP="/tmp/headerdump.txt" — файл, в который будем получать заголовки
SAVEFILE="/tmp/httpfile" — файл с сервера, который мы хотим получить
HTTPSTATUS="" — переменная для статуса (состояния) HTTP

curl -o $SAVEFILE $1 -D $HEADERDUM

пытаемся скачать нужный файл (получить страницу):
-o «имя_файла» — куда сохранять результат
$1 — внутренняя переменная, первый параметр скрипта, при вызове его с командной строки. В своем скрипте надо заменить на свой случай.
можно добавить >/dev/null 2>/dev/null — для красоты, чтоб не вылезали сообщения о процессе и ошибках.

-D <файл> — файл, куда будем копировать заголовки

Файл headerdump.txt выглядит примерно так:

HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Fri, 30 Mar 2018 00:17:23 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.33
Link: ; rel="https://api.w.org/"
Set-Cookie: quick_chat_alias=Anon_240883; path=/
Upgrade: h2,h2c
Vary: Accept-Encoding,User-Agent

В первой строке — интересующая нас информация. Версия протокола (не интересно), код ответа (вот он) и информационное сообщение (не особо надо).

Вычленяем код:

HTTPSTATUS=`cat $HEADERDUMP|head -1|awk '{print $2}'`

1. Берем первую строчку (head -1)
2. Сохраняем в переменную 2-е значение после пробела (разделитель пробел): awk '{print $2}'

Дальше можно анализировать:

    if [ "$HTTPSTATUS" == "200" ];then
	echo "OK"
    else
	echo "HTTP error $HTTPSTATUS"
    fi

Результат

Также, впрочем, можно поступить и для анализа запросов POST, вот тестовый скрипт php для отладки подобных возможностей и скрипт bash

Скачать

Пример с запросом GET
Пример с запросом GET на Гитхаб

Пример с запросом POST
Пример с запросом POST на Гитхаб

Тестовый php скрипт для запроса POST
На Гитхаб

Скачать одним архивом

Мультик про (анти)копирайт

Точнее про то, что копирайт и копирасты это говно, а копирование — не воровство.

Прямая ссылка

Имеются:
— главный герой, получивший от нас (ваших товарищей из параллельного измерения) Хаопринтер, с возможностью 3D-печати и кучей дополнительных опций. Впрочем, он неплохо работает и с дефолтными настройками.
— злобный и жадный копираст, у которого бомбануло
— судья-взяточник, который, естественно, на стороне копираста

В общем, смотрите 🙂

HTML и CSS мелочи

Простой двухколоночный макет на DIV’ах

HTML
CSS
Скачать

Таблица с помощью DIV

HTML
CSS
Скачать

Ограничение размера изображения в CSS

Абсолютный размер в пикселях
По размеру DIV’а
Пример

Источники

Киберфорум
Кот из примера
Еще какие-то

L.S.

Про «Жизнь»

Спрашивают, есть ли что-то подобное в текстовом режиме. Есть, только предупреждаю, там такой говнокодище, что самому смотреть страшно. И некрасиво выглядит, в отличие от графической версии. Если переделаешь, сойдет за зачет, наверное.
Даже картинки не будет.

LIVE.PAS
Архив с экзешником и исходником

C# Пример Charmap (таблицы символов) для различных кодировок.

Очередное студенческое задание, да. Причем опять, то ли препод не совсем адекватный, то ли студент, то ли мы дебилы и не видим чего-то лежащего на поверхности. Потому что «совсем для всех» кодировок, поддерживаемых .NET оно не получается.

Для начала, пришлось задачу все-таки глобально поделить на 3 больших части:
— кодировки однобайтовые (Windows 1251, DOS 866 и т.д.)
— кодировка Unicode
— кодировки многобайтовые, но не Unicode (всякие там японские, китайские и прочие).

Однобайтовые

С ними проще всего. Достаточно перебрать все символы от 0 до 255, перекодировать их последовательно в UTF-16 стандартной функцией класса Encoding и вывести на экран.

Encoding enc = Encoding.GetEncoding(CP);
for (int i = 0; i < 256; i++)
{
	string sChr = enc.GetString(new byte[] { (byte)i });
	//...
}

Отделить их от остальных тоже просто, у объекта Encoding есть свойство IsSingleByte

Unicode

С этим тоже особой сложности нет, весь стандарт открытый, можно взять список диапазонов Юникода прямо с официального сайта. Разобрать, залить в ComboBox и отображать кусками, для больших кусков сделать постраничное листание.

Главная проблема была побороть глюк с отображением символа с кодом > FFFFh Копия, из-за чего пришлось использовать RichTextBox и периодически пересчитывать ему размер в зависимости от используемого шрифта и символа, а также в нужных местах перерисовывать таблицу, чтоб та не расползалась.

Вторая проблема - изобрести какое-то подобие "композитных" шрифтов в BabelMap, т.е. подгружать разные шрифты для разных диапазонов Unicode, поскольку шрифта, абсолютно поддерживающего все символы Unicode нет, он бы получился чудовищно большим. И те, которые есть-то, не маленькие, из нескольких файлов, и весят под сотню мегабайт.

Чтоб ради задания не загадить систему шрифтами, пришлось предусмотреть возможность грузить их из файлов, а тут поджидал уже другой глюк, на этот раз Framework'а Копия.

В общем, процентов 90 кода (да и процессорного времени) уходит на возню со шрифтами и отрисовку таблицы.

Отделить варианты кодирования Unicode от других кодовых страниц тоже несложно, благо всего этих вариантов не так много (UTF16 LE/BE, UTF32 LE/BE, UTF8 и UTF7).

public static bool IsUnicode(int CodePage)
        {

            if (CodePage == 65001 ||
                CodePage == 65000 ||
                CodePage == 1201 ||
                CodePage == 1200 ||
                CodePage == 12000 ||
                CodePage == 12001)
                return true;
            else return false;            
        }


А получить конкретный символ можно вот таким способом:

if ((num >= 0x00d800) && (num <= 0x00dfff))
{
	st = Convert.ToString((char)num);
}
else
{
	st = char.ConvertFromUtf32(num); 
}


if ((num >= 0x00d800) && (num <= 0x00dfff)) - это чтобы не попасть в диапазоны суррогатов, но все-таки на их месте что-то отображать (в шрифтах там обычно пустые символы или знаки ?), а char.ConvertFromUtf32() при попадании в диапазон суррогатов свалится с ошибкой.

Остальные многобайтные кодировки

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

Нет, был, конечно, вариант простого перебора всех возможных вариантов. Максимальное количество байт, необходимых для кодирования символа в указанной кодировке можно получить с помощью Encoding.GetEncoding(cp).GetMaxByteCount(1), но перебрать все возможные значения, например, для массива размером уже в 4 байта, перебирая его, например, так, да еще и фильтруя все лишнее, непомерно долго.

В общем, вроде бы того, что сделали, оказалось достаточно.
Пришлось студенту писать пояснительную записку с источниками 🙂

Исходник примера на GitHub
Скачать

Leha S, NKT

C# Ошибка «Попытка чтения или записи в защищенную память» при работе со шрифтами из PrivateFontCollection

Понадобилось грузить TTF-шрифты из файлов, что делается с помощью PrivateFontCollection.AddFontFile(FileName). И, внезапно, стал проявляться плавающий глюк, возникающий, если периодически пересоздавать объект PrivateFontCollection. Например, при попытке сделать MeasureString(Text, LoadedFont).ToSize(), программа стала вываливаться с ошибкой "Попытка чтения или записи в защищенную память".

Пишут, что это глюк Framework’а и толковое решение только одно, создавать PrivateFontCollection один раз при запуске программы, например, в каком-нибудь статическом классе, и больше ее не трогать. Т.е. подгружать новые шрифты можно, а пересоздавать коллекцию не надо.
Минус решения — можно загадить память подгружаемыми шрифтами, если подгрузить много файлов. Но лучше пока ничего не нашел.

В принципе, можно создать вот такой статический класс, который будет подгружать шрифт в коллекцию, и хранить в Dictionary сопоставление между именем файла и шрифтом в коллекции. Если файл еще не был загружен — подгружать, если был, возвращать позицию в коллекции.

Ну и чтоб два раза не вставать, класс FileFont, который заодно меняет параметры шрифта (размер и начертание).

На Cyberforum’е тоже пока ничего более дельного не предложили.

C# Отображение суррогатной пары (символа с кодом >FFFF)

Известно, что внутренний формат строк в .NET — UTF16, что значит, что символы из основной многоязыковой плоскости (BMP) Unicode, т.е. с кодом меньше FFFF кодируются одним машинным словом, а остальные символы — двумя, одно из которых т.н. суррогат.

Так вот, как оказалось, с отображением суррогатных пар может возникнуть проблема. Контролы Windows Forms могут воспринимать символ с кодом >FFFF не как один символ, как должно быть, а как два. Для примера пробуем отобразить анатолийский иероглиф

с кодом 0x14476

Как видно, TextBox и Label воспринимают символ, как два символа, и, соответственно, его не отображают, а RichTextBox видит, что это один символ, но не отображает его, даже при правильно заданном шрифте.
MessageBox также отображает символ не как один, а как два:

Устранение проблемы с RichTextBox

Для устранения проблемы с RichTextBox достаточно задать нужный шрифт перед тем, как присвоить свойству Text нужные символы.

         int Code = 0x14476; //анатолийский иероглиф        
        Font AnFont = new Font("Anatolian", 24, FontStyle.Regular,
 GraphicsUnit.Pixel, 1);
        private void Form1_Load(object sender, EventArgs e)
        {
            string strSP = char.ConvertFromUtf32(Code);
            
            lblTest.Font = AnFont;
            txtTest.Font = AnFont;                        
            lblFont.Text = AnFont.FontFamily.Name;
            rtbTest.Font = AnFont;

            lblTest.Text = strSP;
            txtTest.Text = strSP;
            rtbTest.Text = strSP;
        }

PictureBox также отрисовывает нужный символ без проблем:

Корень проблемы

А корень проблемы кроется не в C#, .NET и компонентах Windows Forms, а в самой Windows. Пакет обновления SP1 для Windows 7 решает проблему полностью.

Наиболее универсальное решение

Наиболее универсальным решением является использование RichTextBox для работы с такими символами, во всяком случае, это работает даже для Windows XP, для которой ждать патчей, понятное дело, не приходится.
Главное, не забывать про то, что шрифт надо задавать до вставки/присвоения символа.

Обсуждение на Киберфоруме Копия

Пример
Пример на GitHub

Шрифты поддерживающие весь Unicode-диапазон

Или, во всяком случае, большинство символов.

TWBh: шрифт, поддерживающий большинство символов Unicode
Дополнительно:
Композитный шрифт для BabelMap (TWBhBabelCompositeFont.xml)
Композитный шрифт для WPF (TWBh.CompositeFont)
Композитный шрифт для примера простой Charmap для Unicode и однобайтных кодировок (babel.cf)

Universalia самый распространенный максимально поддерживающий Unicode набор шрифтов Ссылка Magnet

Набор шрифтов с почти полной поддержкой Unicode, собранный неизвестным пользователем интернета Что и как поддердживает каждый файл шрифта, есть описание в тектовом файле. Плюс есть композитный шрифт для тестовой программы.

Программа BabelMap Копия
Самый совершенный Charmap для Unicode, поддерживает композитные шрифты и все диапазоны Unicode