Related
How to convert a double into a floating-point string representation without scientific notation in the .NET Framework?
"Small" samples (effective numbers may be of any size, such as 1.5E200 or 1e-200) :
3248971234698200000000000000000000000000000000
0.00000000000000000000000000000000000023897356978234562
None of the standard number formats are like this, and a custom format also doesn't seem to allow having an open number of digits after the decimal separator.
This is not a duplicate of How to convert double to string without the power to 10 representation (E-05) because the answers given there do not solve the issue at hand. The accepted solution in this question was to use a fixed point (such as 20 digits), which is not what I want. A fixed point formatting and trimming the redundant 0 doesn't solve the issue either because the max width for fixed width is 99 characters.
Note: the solution has to deal correctly with custom number formats (e.g. other decimal separator, depending on culture information).
Edit: The question is really only about displaing aforementioned numbers. I'm aware of how floating point numbers work and what numbers can be used and computed with them.
For a general-purpose¹ solution you need to preserve 339 places:
doubleValue.ToString("0." + new string('#', 339))
The maximum number of non-zero decimal digits is 16. 15 are on the right side of the decimal point. The exponent can move those 15 digits a maximum of 324 places to the right. (See the range and precision.)
It works for double.Epsilon, double.MinValue, double.MaxValue, and anything in between.
The performance will be much greater than the regex/string manipulation solutions since all formatting and string work is done in one pass by unmanaged CLR code. Also, the code is much simpler to prove correct.
For ease of use and even better performance, make it a constant:
public static class FormatStrings
{
public const string DoubleFixedPoint = "0.###################################################################################################################################################################################################################################################################################################################################################";
}
¹ Update: I mistakenly said that this was also a lossless solution. In fact it is not, since ToString does its normal display rounding for all formats except r. Live example. Thanks, #Loathing! Please see Lothing’s answer if you need the ability to roundtrip in fixed point notation (i.e, if you’re using .ToString("r") today).
I had a similar problem and this worked for me:
doubleValue.ToString("F99").TrimEnd('0')
F99 may be overkill, but you get the idea.
This is a string parsing solution where the source number (double) is converted into a string and parsed into its constituent components. It is then reassembled by rules into the full-length numeric representation. It also accounts for locale as requested.
Update: The tests of the conversions only include single-digit whole numbers, which is the norm, but the algorithm also works for something like: 239483.340901e-20
using System;
using System.Text;
using System.Globalization;
using System.Threading;
public class MyClass
{
public static void Main()
{
Console.WriteLine(ToLongString(1.23e-2));
Console.WriteLine(ToLongString(1.234e-5)); // 0.00010234
Console.WriteLine(ToLongString(1.2345E-10)); // 0.00000001002345
Console.WriteLine(ToLongString(1.23456E-20)); // 0.00000000000000000100023456
Console.WriteLine(ToLongString(5E-20));
Console.WriteLine("");
Console.WriteLine(ToLongString(1.23E+2)); // 123
Console.WriteLine(ToLongString(1.234e5)); // 1023400
Console.WriteLine(ToLongString(1.2345E10)); // 1002345000000
Console.WriteLine(ToLongString(-7.576E-05)); // -0.00007576
Console.WriteLine(ToLongString(1.23456e20));
Console.WriteLine(ToLongString(5e+20));
Console.WriteLine("");
Console.WriteLine(ToLongString(9.1093822E-31)); // mass of an electron
Console.WriteLine(ToLongString(5.9736e24)); // mass of the earth
Console.ReadLine();
}
private static string ToLongString(double input)
{
string strOrig = input.ToString();
string str = strOrig.ToUpper();
// if string representation was collapsed from scientific notation, just return it:
if (!str.Contains("E")) return strOrig;
bool negativeNumber = false;
if (str[0] == '-')
{
str = str.Remove(0, 1);
negativeNumber = true;
}
string sep = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
char decSeparator = sep.ToCharArray()[0];
string[] exponentParts = str.Split('E');
string[] decimalParts = exponentParts[0].Split(decSeparator);
// fix missing decimal point:
if (decimalParts.Length==1) decimalParts = new string[]{exponentParts[0],"0"};
int exponentValue = int.Parse(exponentParts[1]);
string newNumber = decimalParts[0] + decimalParts[1];
string result;
if (exponentValue > 0)
{
result =
newNumber +
GetZeros(exponentValue - decimalParts[1].Length);
}
else // negative exponent
{
result =
"0" +
decSeparator +
GetZeros(exponentValue + decimalParts[0].Length) +
newNumber;
result = result.TrimEnd('0');
}
if (negativeNumber)
result = "-" + result;
return result;
}
private static string GetZeros(int zeroCount)
{
if (zeroCount < 0)
zeroCount = Math.Abs(zeroCount);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < zeroCount; i++) sb.Append("0");
return sb.ToString();
}
}
You could cast the double to decimal and then do ToString().
(0.000000005).ToString() // 5E-09
((decimal)(0.000000005)).ToString() // 0,000000005
I haven't done performance testing which is faster, casting from 64-bit double to 128-bit decimal or a format string of over 300 chars. Oh, and there might possibly be overflow errors during conversion, but if your values fit a decimal this should work fine.
Update: The casting seems to be a lot faster. Using a prepared format string as given in the other answer, formatting a million times takes 2.3 seconds and casting only 0.19 seconds. Repeatable. That's 10x faster. Now it's only about the value range.
This is what I've got so far, seems to work, but maybe someone has a better solution:
private static readonly Regex rxScientific = new Regex(#"^(?<sign>-?)(?<head>\d+)(\.(?<tail>\d*?)0*)?E(?<exponent>[+\-]\d+)$", RegexOptions.IgnoreCase|RegexOptions.ExplicitCapture|RegexOptions.CultureInvariant);
public static string ToFloatingPointString(double value) {
return ToFloatingPointString(value, NumberFormatInfo.CurrentInfo);
}
public static string ToFloatingPointString(double value, NumberFormatInfo formatInfo) {
string result = value.ToString("r", NumberFormatInfo.InvariantInfo);
Match match = rxScientific.Match(result);
if (match.Success) {
Debug.WriteLine("Found scientific format: {0} => [{1}] [{2}] [{3}] [{4}]", result, match.Groups["sign"], match.Groups["head"], match.Groups["tail"], match.Groups["exponent"]);
int exponent = int.Parse(match.Groups["exponent"].Value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
StringBuilder builder = new StringBuilder(result.Length+Math.Abs(exponent));
builder.Append(match.Groups["sign"].Value);
if (exponent >= 0) {
builder.Append(match.Groups["head"].Value);
string tail = match.Groups["tail"].Value;
if (exponent < tail.Length) {
builder.Append(tail, 0, exponent);
builder.Append(formatInfo.NumberDecimalSeparator);
builder.Append(tail, exponent, tail.Length-exponent);
} else {
builder.Append(tail);
builder.Append('0', exponent-tail.Length);
}
} else {
builder.Append('0');
builder.Append(formatInfo.NumberDecimalSeparator);
builder.Append('0', (-exponent)-1);
builder.Append(match.Groups["head"].Value);
builder.Append(match.Groups["tail"].Value);
}
result = builder.ToString();
}
return result;
}
// test code
double x = 1.0;
for (int i = 0; i < 200; i++) {
x /= 10;
}
Console.WriteLine(x);
Console.WriteLine(ToFloatingPointString(x));
The problem using #.###...### or F99 is that it doesn't preserve precision at the ending decimal places, e.g:
String t1 = (0.0001/7).ToString("0." + new string('#', 339)); // 0.0000142857142857143
String t2 = (0.0001/7).ToString("r"); // 1.4285714285714287E-05
The problem with DecimalConverter.cs is that it is slow. This code is the same idea as Sasik's answer, but twice as fast. Unit test method at bottom.
public static class RoundTrip {
private static String[] zeros = new String[1000];
static RoundTrip() {
for (int i = 0; i < zeros.Length; i++) {
zeros[i] = new String('0', i);
}
}
private static String ToRoundTrip(double value) {
String str = value.ToString("r");
int x = str.IndexOf('E');
if (x < 0) return str;
int x1 = x + 1;
String exp = str.Substring(x1, str.Length - x1);
int e = int.Parse(exp);
String s = null;
int numDecimals = 0;
if (value < 0) {
int len = x - 3;
if (e >= 0) {
if (len > 0) {
s = str.Substring(0, 2) + str.Substring(3, len);
numDecimals = len;
}
else
s = str.Substring(0, 2);
}
else {
// remove the leading minus sign
if (len > 0) {
s = str.Substring(1, 1) + str.Substring(3, len);
numDecimals = len;
}
else
s = str.Substring(1, 1);
}
}
else {
int len = x - 2;
if (len > 0) {
s = str[0] + str.Substring(2, len);
numDecimals = len;
}
else
s = str[0].ToString();
}
if (e >= 0) {
e = e - numDecimals;
String z = (e < zeros.Length ? zeros[e] : new String('0', e));
s = s + z;
}
else {
e = (-e - 1);
String z = (e < zeros.Length ? zeros[e] : new String('0', e));
if (value < 0)
s = "-0." + z + s;
else
s = "0." + z + s;
}
return s;
}
private static void RoundTripUnitTest() {
StringBuilder sb33 = new StringBuilder();
double[] values = new [] { 123450000000000000.0, 1.0 / 7, 10000000000.0/7, 100000000000000000.0/7, 0.001/7, 0.0001/7, 100000000000000000.0, 0.00000000001,
1.23e-2, 1.234e-5, 1.2345E-10, 1.23456E-20, 5E-20, 1.23E+2, 1.234e5, 1.2345E10, -7.576E-05, 1.23456e20, 5e+20, 9.1093822E-31, 5.9736e24, double.Epsilon };
foreach (int sign in new [] { 1, -1 }) {
foreach (double val in values) {
double val2 = sign * val;
String s1 = val2.ToString("r");
String s2 = ToRoundTrip(val2);
double val2_ = double.Parse(s2);
double diff = Math.Abs(val2 - val2_);
if (diff != 0) {
throw new Exception("Value {0} did not pass ToRoundTrip.".Format2(val.ToString("r")));
}
sb33.AppendLine(s1);
sb33.AppendLine(s2);
sb33.AppendLine();
}
}
}
}
The obligatory Logarithm-based solution. Note that this solution, because it involves doing math, may reduce the accuracy of your number a little bit. Not heavily tested.
private static string DoubleToLongString(double x)
{
int shift = (int)Math.Log10(x);
if (Math.Abs(shift) <= 2)
{
return x.ToString();
}
if (shift < 0)
{
double y = x * Math.Pow(10, -shift);
return "0.".PadRight(-shift + 2, '0') + y.ToString().Substring(2);
}
else
{
double y = x * Math.Pow(10, 2 - shift);
return y + "".PadRight(shift - 2, '0');
}
}
Edit: If the decimal point crosses non-zero part of the number, this algorithm will fail miserably. I tried for simple and went too far.
In the old days when we had to write our own formatters, we'd isolate the mantissa and exponent and format them separately.
In this article by Jon Skeet (https://csharpindepth.com/articles/FloatingPoint) he provides a link to his DoubleConverter.cs routine that should do exactly what you want. Skeet also refers to this at extracting mantissa and exponent from double in c#.
I have just improvised on the code above to make it work for negative exponential values.
using System;
using System.Text.RegularExpressions;
using System.IO;
using System.Text;
using System.Threading;
namespace ConvertNumbersInScientificNotationToPlainNumbers
{
class Program
{
private static string ToLongString(double input)
{
string str = input.ToString(System.Globalization.CultureInfo.InvariantCulture);
// if string representation was collapsed from scientific notation, just return it:
if (!str.Contains("E")) return str;
var positive = true;
if (input < 0)
{
positive = false;
}
string sep = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
char decSeparator = sep.ToCharArray()[0];
string[] exponentParts = str.Split('E');
string[] decimalParts = exponentParts[0].Split(decSeparator);
// fix missing decimal point:
if (decimalParts.Length == 1) decimalParts = new string[] { exponentParts[0], "0" };
int exponentValue = int.Parse(exponentParts[1]);
string newNumber = decimalParts[0].Replace("-", "").
Replace("+", "") + decimalParts[1];
string result;
if (exponentValue > 0)
{
if (positive)
result =
newNumber +
GetZeros(exponentValue - decimalParts[1].Length);
else
result = "-" +
newNumber +
GetZeros(exponentValue - decimalParts[1].Length);
}
else // negative exponent
{
if (positive)
result =
"0" +
decSeparator +
GetZeros(exponentValue + decimalParts[0].Replace("-", "").
Replace("+", "").Length) + newNumber;
else
result =
"-0" +
decSeparator +
GetZeros(exponentValue + decimalParts[0].Replace("-", "").
Replace("+", "").Length) + newNumber;
result = result.TrimEnd('0');
}
float temp = 0.00F;
if (float.TryParse(result, out temp))
{
return result;
}
throw new Exception();
}
private static string GetZeros(int zeroCount)
{
if (zeroCount < 0)
zeroCount = Math.Abs(zeroCount);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < zeroCount; i++) sb.Append("0");
return sb.ToString();
}
public static void Main(string[] args)
{
//Get Input Directory.
Console.WriteLine(#"Enter the Input Directory");
var readLine = Console.ReadLine();
if (readLine == null)
{
Console.WriteLine(#"Enter the input path properly.");
return;
}
var pathToInputDirectory = readLine.Trim();
//Get Output Directory.
Console.WriteLine(#"Enter the Output Directory");
readLine = Console.ReadLine();
if (readLine == null)
{
Console.WriteLine(#"Enter the output path properly.");
return;
}
var pathToOutputDirectory = readLine.Trim();
//Get Delimiter.
Console.WriteLine("Enter the delimiter;");
var columnDelimiter = (char)Console.Read();
//Loop over all files in the directory.
foreach (var inputFileName in Directory.GetFiles(pathToInputDirectory))
{
var outputFileWithouthNumbersInScientificNotation = string.Empty;
Console.WriteLine("Started operation on File : " + inputFileName);
if (File.Exists(inputFileName))
{
// Read the file
using (var file = new StreamReader(inputFileName))
{
string line;
while ((line = file.ReadLine()) != null)
{
String[] columns = line.Split(columnDelimiter);
var duplicateLine = string.Empty;
int lengthOfColumns = columns.Length;
int counter = 1;
foreach (var column in columns)
{
var columnDuplicate = column;
try
{
if (Regex.IsMatch(columnDuplicate.Trim(),
#"^[+-]?[0-9]+(\.[0-9]+)?[E]([+-]?[0-9]+)$",
RegexOptions.IgnoreCase))
{
Console.WriteLine("Regular expression matched for this :" + column);
columnDuplicate = ToLongString(Double.Parse
(column,
System.Globalization.NumberStyles.Float));
Console.WriteLine("Converted this no in scientific notation " +
"" + column + " to this number " +
columnDuplicate);
}
}
catch (Exception)
{
}
duplicateLine = duplicateLine + columnDuplicate;
if (counter != lengthOfColumns)
{
duplicateLine = duplicateLine + columnDelimiter.ToString();
}
counter++;
}
duplicateLine = duplicateLine + Environment.NewLine;
outputFileWithouthNumbersInScientificNotation = outputFileWithouthNumbersInScientificNotation + duplicateLine;
}
file.Close();
}
var outputFilePathWithoutNumbersInScientificNotation
= Path.Combine(pathToOutputDirectory, Path.GetFileName(inputFileName));
//Create Directory If it does not exist.
if (!Directory.Exists(pathToOutputDirectory))
Directory.CreateDirectory(pathToOutputDirectory);
using (var outputFile =
new StreamWriter(outputFilePathWithoutNumbersInScientificNotation))
{
outputFile.Write(outputFileWithouthNumbersInScientificNotation);
outputFile.Close();
}
Console.WriteLine("The transformed file is here :" +
outputFilePathWithoutNumbersInScientificNotation);
}
}
}
}
}
This code takes an input directory and based on the delimiter converts all values in scientific notation to numeric format.
Thanks
try this one:
public static string DoubleToFullString(double value,
NumberFormatInfo formatInfo)
{
string[] valueExpSplit;
string result, decimalSeparator;
int indexOfDecimalSeparator, exp;
valueExpSplit = value.ToString("r", formatInfo)
.ToUpper()
.Split(new char[] { 'E' });
if (valueExpSplit.Length > 1)
{
result = valueExpSplit[0];
exp = int.Parse(valueExpSplit[1]);
decimalSeparator = formatInfo.NumberDecimalSeparator;
if ((indexOfDecimalSeparator
= valueExpSplit[0].IndexOf(decimalSeparator)) > -1)
{
exp -= (result.Length - indexOfDecimalSeparator - 1);
result = result.Replace(decimalSeparator, "");
}
if (exp >= 0) result += new string('0', Math.Abs(exp));
else
{
exp = Math.Abs(exp);
if (exp >= result.Length)
{
result = "0." + new string('0', exp - result.Length)
+ result;
}
else
{
result = result.Insert(result.Length - exp, decimalSeparator);
}
}
}
else result = valueExpSplit[0];
return result;
}
Being millions of programmers world wide, it's always a good practice to try search if someone has bumped into your problem already. Sometimes there's solutions are garbage, which means it's time to write your own, and sometimes there are great, such as the following:
http://www.yoda.arachsys.com/csharp/DoubleConverter.cs
(details: http://www.yoda.arachsys.com/csharp/floatingpoint.html)
string strdScaleFactor = dScaleFactor.ToString(); // where dScaleFactor = 3.531467E-05
decimal decimalScaleFactor = Decimal.Parse(strdScaleFactor, System.Globalization.NumberStyles.Float);
I don't know if my answer to the question can still be helpful. But in this case I suggest the "decomposition of the double variable into decimal places" to store it in an Array / Array of data of type String.
This process of decomposition and storage in parts (number by number) from double to string, would basically work with the use of two loops and an "alternative" (if you thought of workaround, I think you got it), where the first loop will extract the values from double without converting to String, resulting in blessed scientific notation and storing number by number in an Array. And this will be done using MOD - the same method to check a palindrome number, which would be for example:
String[] Array_ = new double[ **here you will put an extreme value of places your DOUBLE can reach, you must have a prediction**];
for (int i = 0, variableDoubleMonstrous > 0, i++){
x = variableDoubleMonstrous %10;
Array_[i] = x;
variableDoubleMonstrous /= 10;
}
And the second loop to invert the Array values (because in this process of checking a palindrome, the values invert from the last place, to the first, from the penultimate to the second and so on. Remember?) to get the original value:
String[] ArrayFinal = new String[the same number of "places" / indices of the other Array / Data array];
int lengthArray = Array_.Length;
for (int i = 0, i < Array_.Length, i++){
FinalArray[i] = Array_[lengthArray - 1];
lengthArray--;
}
***Warning: There's a catch that I didn't pay attention to. In that case there will be no "." (floating point decimal separator or double), so this solution is not generalized. But if it is really important to use decimal separators, unfortunately the only possibility (If done well, it will have a great performance) is:
**Use a routine to get the position of the decimal point of the original value, the one with scientific notation - the important thing is that you know that this floating point is before a number such as the "Length" position x, and after a number such as the y position - extracting each digit using the loops - as shown above - and at the end "export" the data from the last Array to another one, including the decimal place divider (the comma, or the period , if variable decimal, double or float) in the imaginary position that was in the original variable, in the "real" position of that matrix.
*** The concept of position is, find out how many numbers occur before the decimal point, so with this information you will be able to store in the String Array the point in the real position.
NEEDS THAT CAN BE MADE:
But then you ask:
But what about when I'm going to convert String to a floating point value?
My answer is that you use the second matrix of this entire process (the one that receives the inversion of the first matrix that obtains the numbers by the palindrome method) and use it for the conversion, but always making sure, when necessary, of the position of the decimal place in future situations, in case this conversion (Double -> String) is needed again.
But what if the problem is to use the value of the converted Double (Array of Strings) in a calculation. Then in this case you went around in circles. Well, the original variable will work anyway even with scientific notation. The only difference between floating point and decimal variable types is in the rounding of values, which depending on the purpose, it will only be necessary to change the type of data used, but it is dangerous to have a significant loss of information, look here
I could be wrong, but isn't it like this?
data.ToString("n");
http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx
i think you need only to use IFormat with
ToString(doubleVar, System.Globalization.NumberStyles.Number)
example:
double d = double.MaxValue;
string s = d.ToString(d, System.Globalization.NumberStyles.Number);
My solution was using the custom formats.
try this:
double d;
d = 1234.12341234;
d.ToString("#########0.#########");
Just to build on what jcasso said what you can do is to adjust your double value by changing the exponent so that your favorite format would do it for you, apply the format, and than pad the result with zeros to compensate for the adjustment.
This works fine for me...
double number = 1.5E+200;
string s = number.ToString("#");
//Output: "150000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
I wonder what would be the best way to format numbers so that the NumberGroupSeparator would work not only on the integer part to the left of the comma, but also on the fractional part, on the right of the comma.
Math.PI.ToString("###,###,##0.0##,###,###,###") // As documented ..
// ..this doesn't work
3.14159265358979 // result
3.141,592,653,589,79 // desired result
As documented on MSDN the NumberGroupSeparator works only to the left of the comma. I wonder why??
A little clunky, and it won't work for scientific numbers but here is a try:
class Program
{
static void Main(string[] args)
{
var π=Math.PI*10000;
Debug.WriteLine(Display(π));
// 31,415.926,535,897,931,899
}
static string Display(double x)
{
int s=Math.Sign(x);
x=Math.Abs(x);
StringBuilder text=new StringBuilder();
var y=Math.Truncate(x);
text.Append((s*y).ToString("#,#"));
x-=y;
if (x>0)
{
// 15 decimal places is max reasonable precision
y=Math.Truncate(x*Math.Pow(10, 15));
text.Append(".");
text.Append(y.ToString("#,#").TrimEnd('0'));
}
return text.ToString();
}
}
It might be best to work with the string generated by your .ToString():
class Program
{
static string InsertSeparators(string s)
{
string decSeparator = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator;
int separatorPos = s.IndexOf(decSeparator);
if (separatorPos >= 0)
{
string decPart = s.Substring(separatorPos + decSeparator.Length);
// split the string into parts of 3 or less characters
List<String> parts = new List<String>();
for (int i = 0; i < decPart.Length; i += 3)
{
string part = "";
for (int j = 0; (j < 3) && (i + j < decPart.Length); j++)
{
part += decPart[i + j];
}
parts.Add(part);
}
string groupSeparator = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberGroupSeparator;
s = s.Substring(0, separatorPos) + decSeparator + String.Join(groupSeparator, parts);
}
return s;
}
static void Main(string[] args)
{
for (int n = 0; n < 15; n++)
{
string s = Math.PI.ToString("0." + new string('#', n));
Console.WriteLine(InsertSeparators(s));
}
Console.ReadLine();
}
}
Outputs:
3
3.1
3.14
3.142
3.141,6
3.141,59
3.141,593
3.141,592,7
3.141,592,65
3.141,592,654
3.141,592,653,6
3.141,592,653,59
3.141,592,653,59
3.141,592,653,589,8
3.141,592,653,589,79
OK, not my strong side, but I guess this may be my best bet:
string input = Math.PI.ToString();
string decSeparator = System.Threading.Thread.CurrentThread
.CurrentCulture.NumberFormat.NumberGroupSeparator;
Regex RX = new Regex(#"([0-9]{3})");
string result = RX.Replace(input , #"$1" + decSeparator);
Thanks for listening..
I am looking to generate a Sequence Number in this format
00000A
00000B
00000B
and so on till
00000Z
and then
00001A
00001B
00001C
...
00001Z
...
00010A
till
99999Z
I know that I can generate Max 2.6 million rows using this method but I guess that is enough
so, if I have the a String, lets say 26522C, Now i want the next number as 26522D
or If I have 34287Z, i want 34288A
I can write the Algorithm about it but there will be lots of parsing of the input string characters by characters
I was wondering is there any easier way of doing it
String GetNextNumberInSequence(String inputString)
{
if (inputString.Length == 6)
{
var charArray = inputString.ToCharArray();
char[] inputChars = { charArray[0], charArray[1], charArray[2],charArray[3],charArray[4],charArray[5] };
if(Char.IsDigit(charArray[5]))
{
//Parse first 5 characters
}
}
}
private static String GetNextNumberInSequence(String inputString)
{
var integerpart = int.Parse(inputString.Substring(0, 5));
var characterPart = inputString[5];
if (characterPart == 'Z')
return string.Format("{0}{1}", (++integerpart).ToString("D5"), "A");
var nextChar = (char)(characterPart + 1);
return string.Format("{0}{1}", (integerpart).ToString("D5"), nextChar.ToString());
}
You can achieve this by converting a number to Base36.
Take a look at this sample:
private const string CharList = "0123456789abcdefghijklmnopqrstuvwxyz";
public static String Base36Encode(long input, char paddingChar, int totalWidth)
{
char[] clistarr = CharList.ToCharArray();
var result = new Stack<char>();
while (input != 0)
{
result.Push(clistarr[input % 36]);
input /= 36;
}
return new string(result.ToArray()).PadLeft(totalWidth, paddingChar).ToUpper();
}
and then use it this way:
for(int i = 0; i < 1000; i++)
{
Debug.WriteLine(Base36Encode(i, '0', 6));
}
which will produce this:
000000, 000001, 000002, 000003, 000004, 000005, 000006, 000007, 000008, 000009, 00000A, 00000B, 00000C, 00000D, 00000E, 00000F, 00000G, 00000H, 00000I, 00000J, 00000K, 00000L, 00000M, 00000N, 00000O, 00000P, 00000Q, 00000R, 00000S, 00000T, 00000U, 00000V, 00000W, 00000X, 00000Y, 00000Z, 000010, 000011, 000012, 000013, 000014, 000015, 000016, 000017, 000018, 000019, 00001A, 00001B, 00001C, 00001D, 00001E, 00001F, 00001G, 00001H, 00001I, 00001J, 00001K, 00001L, 00001M, 00001N, 00001O, 00001P, 00001Q, 00001R, 00001S, 00001T, 00001U, 00001V, 00001W, 00001X, 00001Y, 00001Z, 000020, 000021, 000022, 000023, 000024, 000025, 000026, 000027, 000028, 000029, 00002A, 00002B, 00002C, 00002D, 00002E, 00002F, 00002G, 00002H, 00002I, 00002J, 00002K, 00002L, 00002M, 00002N, 00002O, 00002P, 00002Q, 00002R, 00002S, 00002T...
and the positive thing about this approach is that you can convert this back to number by using:
public static Int64 Base36Decode(string input)
{
var reversed = input.ToLower().Reverse();
long result = 0;
int pos = 0;
foreach (char c in reversed)
{
result += CharList.IndexOf(c) * (long)Math.Pow(36, pos);
pos++;
}
return result;
}
For example if I have...
string a = "personil";
string b = "personal";
I would like to get...
string c = "person[i]l";
However it is not necessarily a single character. I could be like this too...
string a = "disfuncshunal";
string b = "dysfunctional";
For this case I would want to get...
string c = "d[isfuncshu]nal";
Another example would be... (Notice that the length of both words are different.)
string a = "parralele";
string b = "parallel";
string c = "par[ralele]";
Another example would be...
string a = "ato";
string b = "auto";
string c = "a[]to";
How would I go about doing this?
Edit: The length of the two strings can be different.
Edit: Added additional examples. Credit goes to user Nenad for asking.
I must be very bored today, but I actually made UnitTest that pass all 4 cases (if you did not add some more in the meantime).
Edit: Added 2 edge cases and fix for them.
Edit2: letters that repeat multiple times (and error on those letters)
[Test]
[TestCase("parralele", "parallel", "par[ralele]")]
[TestCase("personil", "personal", "person[i]l")]
[TestCase("disfuncshunal", "dysfunctional", "d[isfuncshu]nal")]
[TestCase("ato", "auto", "a[]to")]
[TestCase("inactioned", "inaction", "inaction[ed]")]
[TestCase("refraction", "fraction", "[re]fraction")]
[TestCase("adiction", "ad[]diction", "ad[]iction")]
public void CompareStringsTest(string attempted, string correct, string expectedResult)
{
int first = -1, last = -1;
string result = null;
int shorterLength = (attempted.Length < correct.Length ? attempted.Length : correct.Length);
// First - [
for (int i = 0; i < shorterLength; i++)
{
if (correct[i] != attempted[i])
{
first = i;
break;
}
}
// Last - ]
var a = correct.Reverse().ToArray();
var b = attempted.Reverse().ToArray();
for (int i = 0; i < shorterLength; i++)
{
if (a[i] != b[i])
{
last = i;
break;
}
}
if (first == -1 && last == -1)
result = attempted;
else
{
var sb = new StringBuilder();
if (first == -1)
first = shorterLength;
if (last == -1)
last = shorterLength;
// If same letter repeats multiple times (ex: addition)
// and error is on that letter, we have to trim trail.
if (first + last > shorterLength)
last = shorterLength - first;
if (first > 0)
sb.Append(attempted.Substring(0, first));
sb.Append("[");
if (last > -1 && last + first < attempted.Length)
sb.Append(attempted.Substring(first, attempted.Length - last - first));
sb.Append("]");
if (last > 0)
sb.Append(attempted.Substring(attempted.Length - last, last));
result = sb.ToString();
}
Assert.AreEqual(expectedResult, result);
}
Have you tried my DiffLib?
With that library, and the following code (running in LINQPad):
void Main()
{
string a = "disfuncshunal";
string b = "dysfunctional";
var diff = new Diff<char>(a, b);
var result = new StringBuilder();
int index1 = 0;
int index2 = 0;
foreach (var part in diff)
{
if (part.Equal)
result.Append(a.Substring(index1, part.Length1));
else
result.Append("[" + a.Substring(index1, part.Length1) + "]");
index1 += part.Length1;
index2 += part.Length2;
}
result.ToString().Dump();
}
You get this output:
d[i]sfunc[shu]nal
To be honest I don't understand what this gives you, as you seem to completely ignore the changed parts in the b string, only dumping the relevant portions of the a string.
Here is a complete and working console application that will work for both examples you gave:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
string a = "disfuncshunal";
string b = "dysfunctional";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i])
{
sb.Append("[");
sb.Append(a[i]);
sb.Append("]");
continue;
}
sb.Append(a[i]);
}
var str = sb.ToString();
var startIndex = str.IndexOf("[");
var endIndex = str.LastIndexOf("]");
var start = str.Substring(0, startIndex + 1);
var mid = str.Substring(startIndex + 1, endIndex - 1);
var end = str.Substring(endIndex);
Console.WriteLine(start + mid.Replace("[", "").Replace("]", "") + end);
}
}
}
it will not work if you want to display more than one entire section of the mismatched word.
You did not specify what to do if the strings were of different lengths, but here is a solution to the problem when the strings are of equal length:
private string Compare(string string1, string string2) {
//This only works if the two strings are the same length..
string output = "";
bool mismatch = false;
for (int i = 0; i < string1.Length; i++) {
char c1 = string1[i];
char c2 = string2[i];
if (c1 == c2) {
if (mismatch) {
output += "]" + c1;
mismatch = false;
} else {
output += c1;
}
} else {
if (mismatch) {
output += c1;
} else {
output += "[" + c1;
mismatch = true;
}
}
}
return output;
}
Not really good approach but as an exercise in using LINQ: task seem to be find matching prefix and suffix for 2 strings, return "prefix + [+ middle of first string + suffix.
So you can match prefix (Zip + TakeWhile(a==b)), than repeat the same for suffix by reversing both strings and reversing result.
var first = "disfuncshunal";
var second = "dysfunctional";
// Prefix
var zipped = first.ToCharArray().Zip(second.ToCharArray(), (f,s)=> new {f,s});
var prefix = string.Join("",
zipped.TakeWhile(c => c.f==c.s).Select(c => c.f));
// Suffix
var zippedReverse = first.ToCharArray().Reverse()
.Zip(second.ToCharArray().Reverse(), (f,s)=> new {f,s});
var suffix = string.Join("",
zippedReverse.TakeWhile(c => c.f==c.s).Reverse().Select(c => c.f));
// Cut and combine.
var middle = first.Substring(prefix.Length,
first.Length - prefix.Length - suffix.Length);
var result = prefix + "[" + middle + "]" + suffix;
Much easier and faster approach is to use 2 for loops (from start to end, and from end to start).
Not entirely sure this is possible, but say I have two strings like so:
"IAmAString-00001"
"IAmAString-00023"
What would be a quick'n'easy way to iterate from IAmAString-0001 to IAmAString-00023 by moving up the index of just the numbers on the end?
The problem is a bit more general than that, for example the string I could be dealing could be of any format but the last bunch of chars will always be numbers, so something like Super_Confusing-String#w00t0003 and in that case the last 0003 would be what I'd use to iterate through.
Any ideas?
You can use char.IsDigit:
static void Main(string[] args)
{
var s = "IAmAString-00001";
int index = -1;
for (int i = 0; i < s.Length; i++)
{
if (char.IsDigit(s[i]))
{
index = i;
break;
}
}
if (index == -1)
Console.WriteLine("digits not found");
else
Console.WriteLine("digits: {0}", s.Substring(index));
}
which produces this output:
digits: 00001
string.Format and a for loop should do what you want.
for(int i = 0; i <=23; i++)
{
string.Format("IAmAString-{0:D4}",i);
}
or something close to that (not sitting in front of a compiler).
string start = "IAmAString-00001";
string end = "IAmAString-00023";
// match constant part and ending digits
var matchstart = Regex.Match(start,#"^(.*?)(\d+)$");
int numberstart = int.Parse(matchstart.Groups[2].Value);
var matchend = Regex.Match(end,#"^(.*?)(\d+)$");
int numberend = int.Parse(matchend.Groups[2].Value);
// constant parts must be the same
if (matchstart.Groups[1].Value != matchend.Groups[1].Value)
throw new ArgumentException("");
// create a format string with same number of digits as original
string format = new string('0', matchstart.Groups[2].Length);
for (int ii = numberstart; ii <= numberend; ++ii)
Console.WriteLine(matchstart.Groups[1].Value + ii.ToString(format));
You could use a Regex:
var match=Regex.Match("Super_Confusing-String#w00t0003",#"(?<=(^.*\D)|^)\d+$");
if(match.Success)
{
var val=int.Parse(match.Value);
Console.WriteLine(val);
}
To answer more specifically, you could use named groups to extract what you need:
var match=Regex.Match(
"Super_Confusing-String#w00t0003",
#"(?<prefix>(^.*\D)|^)(?<digits>\d+)$");
if(match.Success)
{
var prefix=match.Groups["prefix"].Value;
Console.WriteLine(prefix);
var val=int.Parse(match.Groups["digits"].Value);
Console.WriteLine(val);
}
If you can assume that the last 5 characters are the number then:
string prefix = "myprefix-";
for (int i=1; i <=23; i++)
{
Console.WriteLine(myPrefix+i.ToString("D5"));
}
This function will find the trailing number.
private int FindTrailingNumber(string str)
{
string numString = "";
int numTest;
for (int i = str.Length - 1; i > 0; i--)
{
char c = str[i];
if (int.TryParse(c.ToString(), out numTest))
{
numString = c + numString;
}
}
return int.Parse(numString);
}
Assuming all your base strings are the same, this would iterate between strings.
string s1 = "asdf123";
string s2 = "asdf127";
int num1 = FindTrailingNumber(s1);
int num2 = FindTrailingNumber(s2);
string strBase = s1.Replace(num1.ToString(), "");
for (int i = num1; i <= num2; i++)
{
Console.WriteLine(strBase + i.ToString());
}
I think it would be better if you do the search from the last (Rick already upvoted you since it was ur logic :-))
static void Main(string[] args)
{
var s = "IAmAString-00001";
int index = -1;
for (int i = s.Length - 1; i >=0; i--)
{
if (!char.IsDigit(s[i]))
{
index = i;
break;
}
}
if (index == -1)
Console.WriteLine("digits not found");
else
Console.WriteLine("digits: {0}", s.Substring(index));
Console.ReadKey();
}
HTH
If the last X numbers are always digits, then:
int x = 5;
string s = "IAmAString-00001";
int num = int.Parse(s.Substring(s.Length - x, x));
Console.WriteLine("Your Number is: {0}", num);
If the last digits can be 3, 4, or 5 in length, then you will need a little more logic:
int x = 0;
string s = "IAmAString-00001";
foreach (char c in s.Reverse())//Use Reverse() so you start with digits only.
{
if(char.IsDigit(c) == false)
break;//If we start hitting non-digit characters, then exit the loop.
++x;
}
int num = int.Parse(s.Substring(s.Length - x, x));
Console.WriteLine("Your Number is: {0}", num);
I'm not good with complicated RegEx. Because of this, I always shy away from it when maximum optimization is unnecessary. The reason for this is RegEx doesn't always parse strings the way you expect it to. If there is and alternate solution that will still run fast then I'd rather go that route as it's easier for me to understand and know that it will work with any combination of strings.
For Example: if you use some of the other solutions presented here with a string like "I2AmAString-000001", then you will get "2000001" as your number instead of "1".