#c# #api #strtol
#c# #API #strtol
Вопрос:
Допустим, у меня есть следующие даты / время в каком-то внутреннем формате, похожем на ISO:
"2011-11-07T11:17"
"--T11:17"
(11:17 утра, без даты, только время)"-11-07"
(7 ноября, без года, без времени)
Разделители являются обязательными и позволяют мне узнать, присутствуют данные или нет. Данные будут установлены в структуру, подобную:
struct MyDate
{
int? Year ;
int? Month ;
int? Day ;
int? Hour ;
int? Minute ;
}
«Самым простым» способом было бы перебирать символ за символом и извлекать данные, если они присутствуют.
Но у меня сложилось впечатление, что должен быть какой-то API для извлечения, например, целого числа и возврата индекса первого нецелого символа (аналогично C strtol
).).
Есть ли strtol
подобная функция в C # или что-то более высокоуровневое для извлечения типизированных данных вместо разбора строки посимвольно?
Ответ №1:
Вы можете использовать DateTime.ParseExact
. Проверьте документацию здесь
Комментарии:
1. Я не думаю, что это сильно помогает в этом случае — или, по крайней мере, вам нужно будет указать все возможные наличие / отсутствие значений.
2. Что ж, я бы предпочел это альтернативе разбору строки вручную, особенно если эти три начальных формата являются единственными, которые нужны @paercebal.
3. Если это только эти три, то я согласен. Если каждое из 6 значений является необязательным, поэтому существует 64 возможных формата, я думаю, что я бы решил это по-другому.
4. Согласен. Если требуются все возможные комбинации, это может стать гораздо более сложным.
Ответ №2:
Я бы сделал это так:
РЕДАКТИРОВАТЬ 2
Теперь включено:
- статические
Parse
иTryParse
методы в структуре myDate 🙂 - зеркальное отображение toString() для упрощения тестирования
- минимальные тестовые наборы для модульного тестирования
- БОНУСНАЯ демонстрация: неявный оператор преобразования из строки в myDate (чтобы вы могли передать строку, где требуется myDate)
Опять же, смотрите его в прямом эфиреhttp://ideone.com/rukw4
using System;
using System.Text;
using System.Text.RegularExpressions;
struct MyDate
{
public int? Year, Month, Day, Hour, Minute;
private static readonly Regex dtRegex = new Regex(
@"^(?<year>d{4})?-(?<month>dd)?-(?<day>dd)?"
@"(?:T(?<hour>dd)?:(?<minute>dd)?)?$",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
public static bool TryParse(string input, out MyDate result)
{
Match match = dtRegex.Match(input);
result = default(MyDate);
if (match.Success)
{
if (match.Groups["year"].Success)
result.Year = Int32.Parse(match.Groups["year"].Value);
if (match.Groups["month"].Success)
result.Month = Int32.Parse(match.Groups["month"].Value);
if (match.Groups["day"].Success)
result.Day = Int32.Parse(match.Groups["day"].Value);
if (match.Groups["hour"].Success)
result.Hour = Int32.Parse(match.Groups["hour"].Value);
if (match.Groups["minute"].Success)
result.Minute = Int32.Parse(match.Groups["minute"].Value);
}
return match.Success;
}
public static MyDate Parse(string input)
{
MyDate resu<
if (!TryParse(input, out result))
throw new ArgumentException(string.Format("Unable to parse MyDate: '{0}'", input));
return resu<
}
public override string ToString()
{
return string.Format("{0:0000}-{1:00}-{2:00}T{3:00}:{4:00}", Year, Month, Day, Hour, Minute);
}
public static implicit operator MyDate(string input)
{
return Parse(input);
}
}
class Program
{
static void Main(string[] args)
{
foreach (var testcase in new [] {
"2011-11-07T11:17",
"-11-07T11:17",
"2011--07T11:17",
"2011-11-T11:17",
"2011-11-07T:17",
"2011-11-07T11:",
// extra:
"--T11:17", // (11:17 am, no date, only time)
"-11-07", // (november the 7th, no year, no time)
// failures:
"2011/11/07 T 11:17",
"no match" })
{
MyDate parsed;
if (MyDate.TryParse(testcase, out parsed))
Console.WriteLine("'{0}' -> Parsed into '{1}'", testcase, parsed);
else
Console.WriteLine("'{0}' -> Parse failure", testcase);
}
}
}
Вывод:
'2011-11-07T11:17' -> Parsed into '2011-11-07T11:17'
'-11-07T11:17' -> Parsed into '-11-07T11:17'
'2011--07T11:17' -> Parsed into '2011--07T11:17'
'2011-11-T11:17' -> Parsed into '2011-11-T11:17'
'2011-11-07T:17' -> Parsed into '2011-11-07T:17'
'2011-11-07T11:' -> Parsed into '2011-11-07T11:'
'--T11:17' -> Parsed into '--T11:17'
'-11-07' -> Parsed into '-11-07T:'
'2011/11/07 T 11:17' -> Parse failure
'no match' -> Parse failure
Комментарии:
1. эмм. Я подумал, что также добавлю тестовые примеры из OP. Также исправлена ошибка
Ответ №3:
Если производительность не вызывает беспокойства, я бы использовал регулярные выражения для вашей задачи.
Используйте (d*)-(d*)-(d*)T(d*):(d*)
как выражение, а затем проанализируйте каждую группу захвата в соответствующем поле вашей структуры, преобразуя пустые строки в null
значения:
var match = Regex.Match(str, @"(d*)-(d*)-(d*)T(d*):(d*)");
var date = new MyDate();
if (match.Groups[1].Value != "") date.Year = int.Parse(match.Groups[1].Value);
if (match.Groups[2].Value != "") date.Month = int.Parse(match.Groups[2].Value);
if (match.Groups[3].Value != "") date.Day = int.Parse(match.Groups[3].Value);
if (match.Groups[4].Value != "") date.Hour = int.Parse(match.Groups[4].Value);
if (match.Groups[5].Value != "") date.Minute = int.Parse(match.Groups[5].Value);
РЕДАКТИРОВАТЬ: Разделители ‘T’ и ‘:’ являются обязательными в этом коде. Если вы хотите иначе, обратитесь к ответу @sehe вместо этого.
Комментарии:
1. ваше регулярное выражение немного неаккуратно («2011-99-231T:» будет соответствовать, как и «Какой-то фиктивный —T9991238: текст»), вы могли бы предварительно скомпилировать его и повысить производительность
2. Я предполагал, что данные верны во всем примере, и постарался сделать пример как можно короче. Конечно, это можно улучшить.
3. что ж … возможно, тогда попробуйте проанализировать три образца, опубликованные в OP?
4. хе-хе, они противоречат его собственному заявлению о том, что разделители являются обязательными 🙂 Хорошо, тогда мое решение ошибочно. Я не буду его переделывать, так как вы отлично поработали в своем ответе
Ответ №4:
ЕСЛИ вы знаете формат даты, который вы можете использовать DateTime.ParseExact
, например… если вам нужно что-то похожее на, strtol
вы можете использовать Convert.ToInt32
.