My string is "2345000012999922"
I want to convert it to: 2345 0000 12 9999 22. The pattern is always de same AAAA BBBB CC DDDD EE but EE is optional and may not be filled.
I tried:
string.format("{0:#### #### ## #### ##});
with no success. I used a long variable instead of string but still with no succed.
Try this:
void Main()
{
var d = decimal.Parse("2345000012999922");
Console.Out.WriteLine("{0:#### #### ## #### ##}", d);
}
First convert to decimal, then use your own strategy.
Formatting of numbers works right-to-left, meaning if you had 2 numbers as follows:
2345000012999922
23450000129999
And we did something like:
void Main()
{
var d1 = decimal.Parse("23450000129999");
var d2 = decimal.Parse("234500001299");
Console.Out.WriteLine("{0:#### #### ## #### ##}", d1);
Console.Out.WriteLine("{0:#### #### ## #### ##}", d2);
Console.Out.WriteLine("{0:0000 0000 00 0000 00}", d1);
Console.Out.WriteLine("{0:0000 0000 00 0000 00}", d2);
}
We'd get:
23 4500 00 1299 99
2345 00 0012 99
0023 4500 00 1299 99
0000 2345 00 0012 99
(Notice the 0-padding).
In a format string, "0" means put the corresponding digit here, if present, otherwise pad with a 0. A "#" means, put the corresponding digit here, if present, otherwise ignore it.
With this in mind, I think your best strategy would be something like:
void Main()
{
var s1 = "23450000129999";
var s2 = "234500001299";
var n1 = s1.Length;
var n2 = s2.Length;
var c = 12;
var f1 = "{0:#### #### ## #### ##}";
var f2 = "{0:#### #### ## ####}";
var d1 = decimal.Parse(s1);
var d2 = decimal.Parse(s2);
Console.Out.WriteLine(n1 > c ? f1 : f2, d1);
Console.Out.WriteLine(n2 > c ? f1 : f2, d2);
}
This will give:
23 4500 00 1299 99
23 4500 00 1299
The idea is that you check the string-length of the input string first. If it is 12, then you have the last optional bit absent, so you use the truncated format-string. If it is more than 12 (or equal to 14) then use the full format-string.
The other approaches such as regex and string manipulation are good approaches too, though I would suspect that they are less-performant. You should test all approaches though, especially if this piece of code will run many, many times (e.g., if you are showing data in a table).
You can improve the readability of the code further using extension methods by defining something like
public static class FormattingHelper
{
public static string GetFormatString(this string s)
{
if (s.Length == 12)
return "{0:#### #### ## ####}";
else
return "{0:#### #### ## #### ##}";
}
}
void Main()
{
var s1 = "23450000129999";
var s2 = "234500001299";
var d1 = decimal.Parse(s1);
var d2 = decimal.Parse(s2);
Console.Out.WriteLine(s1.GetFormatString(), d1);
Console.Out.WriteLine(s2.GetFormatString(), d2);
}
string s = "2345000012999922";
s = s.Insert(14, " ").Insert(10, " ").Insert(8, " ").Insert(4, " ");
Console.WriteLine(s);
Note: Inserting spaces from the end (i.e. indices go down) so that you can use the indices from the original string. If you tried it the other way, you'd have to successively add 1 to each index to account for the new spaces added before the place you are currently adding a space. Not critical, but I think it's easier to understand if the indices match the places to add spaces in the original string.
Just as another way of doing it (for all slightly daft):
string input = "2345000012999922";
string Formatted = new Regex(#"(\d{4})(\d{4})(\d{2})(\d{4})(\d{2})").
replace(input, "$1 $2 $3 $4 $5");
//Formatted = 2345 0000 12 9999 22
Works for me when using long (PowerShell test, should be the same for C#):
PS> '{0:#### #### ## #### ##}' -f 2345000012999922
2345 0000 12 9999 22
string.Format("{0:#### #### ## #### ##}", 2345000012999922)
output
2345 0000 12 9999 22
Edited
This would also work for you
string str = "2345000012999922";
string str2 = string.Format("{0:#### #### ## #### ##}", Convert.ToDouble(str));
Related
I have string pairs as :
s_1 : "He graduated in 1994 with 32 courses"
s_2 : "I graduated in 0000 with 00 courses"
What I want to do is modify s_2, such that 0000 gets changed to 1994 and 00 to 32.
modified_s_2 : "I graduated in 1994 with 32 courses"
Basically, 0000...n_times...0 tells about that it's going to be matched with string number with n digits in s_1.
I can implement this by looping.
I am looking for efficient implementation. I think regex implementation would be easy for this.
Note : There can any n numbers in strings, and each number can have any number of digits.
I think you mean this:
var s_1 = "He graduated in 1994 with 32 courses";
var s_2 = "I graduated in 0000 with 00 courses 0000";
//// I'll find combination of '0's to be replaced
var regexes =
Regex.Matches(s_2, #"\b0+\b")
.OfType<Match>()
.Select(c => new { c.Value, Reg = new Regex(c.Value.Replace("0", #"\d")) })
.ToList();
//// now I replace each '0's combination with first match
var curS1 = s_1;
foreach (var regex in regexes)
{
var s1Value = regex.Reg.Match(curS1).Value;
curS1 = regex.Reg.Replace(curS1, "", 1); //// I remove first match of s_1 to don't matched again
s_2 = new Regex(regex.Value).Replace(s_2, s1Value, 1);
}
[ C# Demo ]
A test cases can be:
var s_1 = "He graduated in 1994 with 32 courses then 254 for 1998";
var s_2 = "I graduated in 0000 with 00 courses then 000 for 0000";
That result will be:
I graduated in 1994 with 32 courses then 254 for 1998
Hopefully this can get you started since you're looking for regex. You can modify it to be in a loop for whatever kind of "string pairs" you are using.
This is how the regex looks visually: Regex101 (this is why we do the .Trim() below). I changed it so it's less tied to that specific example and can work with a variety of numbers in different places
var s_1 = "He graduated number 1 in class in 1900 with 20 courses over the course of 12 weeks";
var s_2 = "I graduated number 0 in class in 0000 with 00 courses over the course of 00 weeks";
// Finds the matches in s_1 with the year and the number of courses
// The spaces are important in the regex so we match properly
var regex = new Regex("( \\d{1,} )");
var matches = regex.Matches(s_1);
var lastIndex = 0; // This is necessary so we aren't replacing previously replaced values
foreach(var match in matches.Cast<Match>())
{
// The matched value, removing extra spaces
var trimmedMatch = match.Value.Trim();
// The n-length 0 string in s_2
var zeroString = new String('0', trimmedMatch.Length);
// A simpler way to replace a string within a string
var sb = new StringBuilder(s_2);
var replaceIndex = s_2.IndexOf(zeroString, lastIndex);
sb.Remove(replaceIndex, zeroString.Length);
sb.Insert(replaceIndex, trimmedMatch);
s_2 = sb.ToString();
// This is necessary otherwise we could end up overwriting previously done work
lastIndex = replaceIndex + zeroString.Length;
}
Disclamer: I let you handle the error of if the patern string "00" is not in the string.
As I don't have information about the real performance issue that you encounter in your implementation but you can count the number of digit in each your input and templated output so you know if they match.
string input = "He graduated in 1994 with 32 coursesHe graduated in 1994 with 32 coursesHe graduated in 1994 with 32 courses ";
string ouput = "He 0000 with 00 He in 0000 with 00 He in 0000 with 00";
string regex = #"(\d+)";
var matches = Regex.Matches(input, regex).Cast<Match>();
var tempSB = new StringBuilder(ouput);
foreach(var i in matches)
{
var strI = i.Value;
var strILength = strI.Length;
var template = new string('0', strILength );
var index = ouput.IndexOf(template); // if (index ==-1) exception;
tempSB.Remove(index, strILength);
tempSB.Insert(index, strI);
ouput = tempSB.ToString();
}
For a 50Mo input it take about 10 sec. Thats sound reasonable.
I've modified an example to send & receive from serial, and that works fine.
The device I'm connecting to has three commands I need to work with.
My experience is with C.
MAP - returns a list of field_names, (decimal) values & (hex) addresses
I can keep track of which values are returned as decimal or hex.
Each line is terminated with CR
:: Example:
MEMBERS:10 - number of (decimal) member names
NAME_LENGTH:15 - (decimal) length of each name string
NAME_BASE:0A34 - 10 c-strings of (15) characters each starting at address (0x0A34) (may have junk following each null terminator)
etc.
GET hexaddr hexbytecount - returns a list of 2-char hex values starting from (hexaddr).
The returned bytes are a mix of bytes/ints/longs, and null terminated c-strings terminated with CR
:: Example::
get 0a34 10 -- will return
0A34< 54 65 73 74 20 4D 65 20 4F 75 74 00 40 D3 23 0B
This happens to be 'Test Me Out'(00) followed by junk
etc.
PUT hexaddr hexbytevalue {{value...} {value...}} sends multiple hex byte values separated by spaces starting at hex address, terminated by CR/LF
These bytes are a mix of bytes/ints/longs, and null terminated c-strings :: Example:
put 0a34 50 75 73 68 - (ascii Push)
Will replace the first 4-chars at 0x0A34 to become 'Push Me Out'
SAVED OK
See my answer previously about serial handling, which might be useful Serial Port Polling and Data handling
to convert your response to actual text :-
var s = "0A34 < 54 65 73 74 20 4D 65 20 4F 75 74 00 40 D3 23 0B";
var hex = s.Substring(s.IndexOf("<") + 1).Trim().Split(new char[] {' '});
var numbers = hex.Select(h => Convert.ToInt32(h, 16)).ToList();
var toText = String.Join("",numbers.TakeWhile(n => n!=0)
.Select(n => Char.ConvertFromUtf32(n)).ToArray());
Console.WriteLine(toText);
which :-
skips through the string till after the < character, then splits the rest into hex string
then, converts each hex string into ints ( base 16 )
then, takes each number till it finds a 0 and converts each number to text (using UTF32 encoding)
then, we join all the converted strings together to recreate the original text
alternatively, more condensed
var hex = s.Substring(s.IndexOf("<") + 1).Trim().Split(new char[] {' '});
var bytes = hex.Select(h => (byte) Convert.ToInt32(h, 16)).TakeWhile(n => n != 0);
var toText = Encoding.ASCII.GetString(bytes.ToArray());
for converting to hex from a number :-
Console.WriteLine(123.ToString("X"));
Console.WriteLine(123.ToString("X4"));
Console.WriteLine(123.ToString("X8"));
Console.WriteLine(123.ToString("x4"));
also you will find playing with hex data is well documented at https://msdn.microsoft.com/en-us/library/bb311038.aspx
I am new to C# and trying to lean how to filter data that I read from a file. I have a file that I read from that has data similer to the follwoing:
3 286 858 95.333 0.406 0.427 87.00 348 366 4 b
9 23 207 2.556 0.300 1.00 1.51 62 207 41 a
9 37 333 4.111 0.390 0.811 2.03 130 270 64 a
10 21 210 2.100 0.348 0.757 3.17 73 159 23 a
9 79 711 8.778 0.343 0.899 2.20 244 639 111 a
10 66 660 6.600 0.324 0.780 2.25 214 515 95 a
When I read these data, some of them have Carriage return Or Line Feed characters hidden in them. Can you please tell me if there is a way to remove them. For example, one of my variable may hold the the following value due to a newline character in them:
mystringval = "9
"
I want this mystringval variable to be converted back to
mystringval = "9"
If you want to get rid of all special characters, you can learn regular expressions and use Regex.Replace.
var value = "&*^)#abcd.";
var filtered = System.Text.RegularExpressions.Regex.Replace(value, #"[^\w]", "");
REGEXPLANATION
the # before the string means that you're using a literal string and c# escape sequences don't work, leaving only the regex escape sequences
[^abc] matches all characters that are not a, b, or c(to replace them with empty space)
\w is a special regex code that means a letter, number, or underscore
you can also use #"[^A-Za-z0-9\.]" which will filter letters, numbers and decimal. See http://rubular.com/ for more details.
As well as using RegEx, you can use LINQ to do something like
var goodCharacters = input
.Replace("\r", " ")
.Replace("\n", " ")
.Where(c => char.IsLetterOrDigit(c) || c == ' ' || c == '.')
.ToArray();
var result = new string(goodCharacters).Trim();
The first two Replace calls will guard against having a number at the end of one line and a number at the start of the next, e.g. "123\r\n987" would otherwise be "123987", whereas I assume you want "123 987".
Try my sample here on ideone.com.
I need to understand how device is calculating checksum for below ASTM string
<STX2P|1|||||||||||||||||||||||||||||||||<CR><ETX>3B<CR><LF>
This string shows that the checksum is "3B", while I am trying to find checksum through below code
public static string ConvertStringToHex(string asciiString)
{
string hex = "";
foreach (char c in asciiString)
{
int tmp = c;
hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
}
return hex;
}
But I am getting "3C" as output. Please help me out how could I find checksum.
How to compute the Checksum in ASTM...
The checksum is computed by adding the binary values of the characters,
keeping the least significant eight bits of the result.
Each character in the message text is added to the checksum (modulo 256).
The checksum is an integer represented by eight bits, it can be considered as two groups of four bits.
The groups of four bits are converted to the ASCII characters of the hexadecimal representation.
The two ASCII characters are transmitted as the checksum, with the most significant character first.
I put the line in the file 3B and ran the script which I'll post below this section. I had to add the > sign to fix the STX String.
leopard:astm rene$ ./astm.sh ./3B Starting ./astm.sh ...
2P|1|||||||||||||||||||||||||||||||||3B
1000100111011 L8B= 111011
LSB used to calculate the MOD 256 L8B
MOD= 00111011
and the checksum is.......
MOD= 00111011 M4B= 0011
--->L4B= 1011
Checksum=3B
#! /bin/ksh
#set -x
#
############################# Variables #######################
#
integer i=0
HEX=00
integer LEN=0
integer FROM=0
MESSAGE=$1
#blind=0 Not blind
#blind=1 blind, it does not see any astm character till it can see again (blind=0)
HEXTMP=/tmp/hex.tmp
BINTMP=/tmp/bin.tmp
#
############################# Functions #######################
#
astm_checksum() {
print"
# The checksum is computed by adding the binary values of the characters,
# keeping the least significant eight bits of the result.
# Each character in the message text is added to the checksum (modulo 256).
# The checksum is an integer represented by eight bits, it can be considered as two groups of four bits.
# The groups of four bits are converted to the ASCII characters of the hexadecimal representation.
# The two ASCII characters are transmitted as the checksum, with the most significant character first.
"
#converting text in Variable VAR to binairy ...
}
code2hex() { #Read and convert text written with codes in ASCII like <STX>
printf "\n\n"
awk '
!/^#/ {
gsub(/<NUL>/,"\x00",$0 )
gsub(/<SOH>/,"\x01",$0 )
gsub(/<STX>/,"\x02",$0 )
gsub(/<ETX>/,"\x03",$0 )
gsub(/<EOT>/,"\x04",$0 )
gsub(/<ENQ>/,"\x05",$0 )
gsub(/<ACK>/,"\x06",$0 )
gsub( /<LF>/,"\x0A",$0 )
gsub( /<FF>/,"\x0C",$0 )
gsub( /<CR>/,"\x0D",$0 )
gsub(/<NAK>/,"\x15",$0 )
gsub(/<SYN>/,"\x16",$0 )
gsub(/<ETB>/,"\x17",$0 )
gsub(/<CAN>/,"\x18",$0 )
gsub(/<ESC>/,"\x1B",$0 )
printf( $0 "\n" )
}
' ${MESSAGE} | hd | cut -c11-58 | tr [:lower:] [:upper:] | xargs | tee ${HEXTMP} | awk '
#example output, one line due to xargs
#02 31 48 7C 5C 5E 26 7C 7C 7C 50 5E 31 7C 7C 7C 7C 7C 7C 7C 50 7C 31 7C 0D 03 31 35 0D 0A
#--- first part to filter out control data from real data ---
BEGIN{ RS=" "; blind = 0 ; printf "ibase=16 ; obase=2 ;scale=0;" }
/0A/ { next } # <LF>
/02/ { printf("00+"); blind = 0 # <STX>
} #Eyes are opened (again), after <STX> we start counting
!/02/ && !/03/ { if ( blind == 0 )
printf( $0"+" )
} #This also includes the <CR> (0D) BEFORE the ETX or ETB!!!
/03/ || /17/ { if ( blind == 0 ) {
printf( $0"\n" )
blind = 1 } #The \n = end line and calculate
} #Blind.. we see nothing till a <STX> is passing again
'| sed 's/+$/\n/p' | tee -a ${HEXTMP} | bc -q | tee ${BINTMP} | while read BIN
do
# The two files tee writes to is for debugging only.
#
# % in bc is modulo but is not described clearly in the man page
# scale is default set to 0 but I set it anyway to be sure.
#
#Binairy
printf "BIN= %08d\n" ${BIN}
#Calculate from where we need to cut the string of bits to have the LSB 8 bits
LEN=$(echo ${BIN} | wc -c ) #Not supported by the QNX Shell
FROM=$(( LEN - 8 )) #Not supported by the QNX Shell
L8B=$(echo ${BIN} | cut -c ${FROM}- )
printf "L8B=%${LEN}d\n" ${L8B}
printf "LSB used to calculate the MOD 256\n"
MOD=$( echo "ibase=2 ; obase=2 ; ${L8B} % 100000000" | bc -q ) #LSB SUM
printf "L8B MOD= %08d\n" ${MOD}
printf "--------------------------- and the checksum is....... \n"
printf "MOD= %08d\n" ${MOD}
M4B=$( printf "%08d\n" ${MOD} | cut -c -4 )
L4B=$( printf "%08d\n" ${MOD} | cut -c 5- )
printf "M4B= $M4B\n--->L4B= $L4B\n"
CD1=$(printf "ibase=2 ; obase=10000 ; ${M4B}\n" | bc -q )
CD2=$(printf "ibase=2 ; obase=10000 ; ${L4B}\n" | bc -q )
printf "Checksum=${CD1}${CD2}\n\n"
done
}
############################# main part ################################
test -r "${MESSAGE}" && (echo "Starting $0 ...";cat ${MESSAGE};code2hex) || echo "ERROR: Cannot read file ${MESSAGE}."
Check this link: https://www.hendricksongroup.com/code_003.aspx
It explains well. Make sure, you use the load-data after STX and including ETX.
E.g. in the line belof:
<STX><Frame Data><CR><ETX><CHECKSUM 1><CHECKSUM 2><CR><LF>
use this part of the data used to calculate the MOD 256 checksum
<Frame Data><CR><ETX>
(sum all load chars and modulo by 256)
EDIT
There are few websites, where you can calculate mod 256 (among others) check sums. e.g.: https://www.scadacore.com/tools/programming-calculators/online-checksum-calculator/
Let's take the string used in the question as the example:
<STX2P|1|||||||||||||||||||||||||||||||||<CR><ETX>3B<CR><LF>
As the string is not correct, there is no closing > after STX, w correct it, though it is not relevant for the checksum calculation.
<STX>2P|1|||||||||||||||||||||||||||||||||<CR><ETX>3B<CR><LF>
Now, the checksum in ASTM 1381 uses the data after and till the checksum including , this part:
P|1|||||||||||||||||||||||||||||||||<CR><ETX>
Let's go to the site and paste the string in the "ASCII Input" text field. As the are replacement for binary data, which can't be displayed as ASCII text, REMOVE <CR><ETX> and press the button AnalyseDataAscii. You will see below calculated chechsum which is wrong as the data is missing <CR><ETX>. Ignore the checksum for the first.
The Hex-Input text field is also updated and displays binary data in the HEX-notation. So add the data for <CR><ETX> at the end of the hex-string in the HEX Input. In Hex CR is 0D and ETX is 03, so add 0D03 and press now the other button AnalyseDataHex:
Now you see the correct checksum, which is indeed 3B, exactly as in the message example.
Obviously the code provided by the question author used not correct part of the string and did't replace the placeholders , by their hexadecimal representations.
I have string like
1 69 / EMP1094467 EMP1094467 : 2 69 / ScreenLysP
here the numeric characters should be replace with empty characters, Llike:
/ EMP1094467
I tried like this
var output = Regex.Replace(input, #"[\d-]", string.Empty);
which produced the following result:
/ EMP
Please suggest a better solution.
You can try using word boundaries:
var input = "1 69 / EMP1094467 EMP1094467 : 2 69 / ScreenLysP ";
var output = Regex.Replace(input, #"\b[\d]+\b", string.Empty);
string.Substring seems fitting here:
var str = "1 69 / EMP1094467";
var result = str.Substring(str.IndexOf("/")); // "/ EMP1094467"