// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Tools.DotNETCommon { public static class StringUtils { /// /// Indents a string by a given indent /// /// The text to indent /// The indent to add to each line /// The indented string public static string Indent(string Text, string Indent) { string Result = ""; if(Text.Length > 0) { Result = Indent + Text.Replace("\n", "\n" + Indent); } return Result; } /// /// Takes a given sentence and wraps it on a word by word basis so that no line exceeds the /// set maximum line length. Words longer than a line are broken up. Returns the sentence as /// a list of individual lines. /// /// The text to be wrapped /// The maximum (non negative) length of the returned sentences /// Receives a list of word-wrapped lines public static List WordWrap(string Text, int MaxWidth) { // Early out if (Text.Length == 0) { return new List(); } string[] Words = Text.Split(' '); List WrappedWords = new List(); string CurrentSentence = string.Empty; foreach (var Word in Words) { // if this is a very large word, split it if (Word.Length > MaxWidth) { // If the current sentence is ready to be written, do that. if (CurrentSentence.Length >= MaxWidth) { // next line and reset sentence WrappedWords.Add(CurrentSentence); CurrentSentence = string.Empty; } // Top up the current line WrappedWords.Add(CurrentSentence + Word.Substring(0, MaxWidth - CurrentSentence.Length)); int length = MaxWidth - CurrentSentence.Length; while (length + MaxWidth < Word.Length) { // Place the starting lengths into their own lines WrappedWords.Add(Word.Substring(length, Math.Min(MaxWidth, Word.Length - length))); length += MaxWidth; } // then the trailing end into the next line CurrentSentence += Word.Substring(length, Math.Min(MaxWidth, Word.Length - length)) + " "; } else { if (CurrentSentence.Length + Word.Length > MaxWidth) { // next line and reset sentence WrappedWords.Add(CurrentSentence); CurrentSentence = string.Empty; } // Add the word to the current sentence. CurrentSentence += Word + " "; } } if (CurrentSentence.Length > 0) { WrappedWords.Add(CurrentSentence); } return WrappedWords; } /// /// Extension method to allow formatting a string to a stringbuilder and appending a newline /// /// The string builder /// Format string, as used for StringBuilder.AppendFormat /// Arguments for the format string public static void AppendLine(this StringBuilder Builder, string Format, params object[] Args) { Builder.AppendFormat(Format, Args); Builder.AppendLine(); } /// /// Formats a list of strings in the style "1, 2, 3 and 4" /// /// List of strings to format /// Formatted list of strings public static string FormatList(string[] Arguments) { StringBuilder Result = new StringBuilder(); if(Arguments.Length > 0) { Result.Append(Arguments[0]); for(int Idx = 1; Idx < Arguments.Length; Idx++) { if(Idx == Arguments.Length - 1) { Result.Append(" and "); } else { Result.Append(", "); } Result.Append(Arguments[Idx]); } } return Result.ToString(); } /// /// Formats a list of strings in the style "1, 2, 3 and 4" /// /// List of strings to format /// Formatted list of strings public static string FormatList(IEnumerable Arguments) { return FormatList(Arguments.ToArray()); } /// /// Parses a hexadecimal digit /// /// Character to parse /// Value of this digit public static bool TryParseHexDigit(char Character, out int Value) { if(Character >= '0' && Character <= '9') { Value = Character - '0'; return true; } else if(Character >= 'a' && Character <= 'f') { Value = 10 + (Character - 'a'); return true; } else if(Character >= 'A' && Character <= 'F') { Value = 10 + (Character - 'A'); return true; } else { Value = 0; return false; } } /// /// Parses a hexadecimal string into an array of bytes /// /// Array of bytes public static byte[] ParseHexString(string Text) { byte[] Bytes; if(!TryParseHexString(Text, out Bytes)) { throw new FormatException(String.Format("Invalid hex string: '{0}'", Text)); } return Bytes; } /// /// Parses a hexadecimal string into an array of bytes /// /// Text to parse /// public static bool TryParseHexString(string Text, out byte[] OutBytes) { if((Text.Length & 1) != 0) { throw new FormatException("Length of hex string must be a multiple of two characters"); } byte[] Bytes = new byte[Text.Length / 2]; for(int Idx = 0; Idx < Text.Length; Idx += 2) { int A, B; if(!TryParseHexDigit(Text[Idx], out A) || !TryParseHexDigit(Text[Idx + 1], out B)) { OutBytes = null; return false; } Bytes[Idx / 2] = (byte)((A << 4) | B); } OutBytes = Bytes; return true; } /// /// Formats an array of bytes as a hexadecimal string /// /// An array of bytes /// String representation of the array public static string FormatHexString(byte[] Bytes) { const string HexDigits = "0123456789abcdef"; char[] Characters = new char[Bytes.Length * 2]; for(int Idx = 0; Idx < Bytes.Length; Idx++) { Characters[Idx * 2 + 0] = HexDigits[Bytes[Idx] >> 4]; Characters[Idx * 2 + 1] = HexDigits[Bytes[Idx] & 15]; } return new string(Characters); } } }