I need to prevent a string from exceeding a certain length and, if it does, truncate the last part of the string.
I'm using GUI.TextField to get the string from the user.
Wrap it with a property to handle truncation:
public SomeClass {
private const int MaxLength = 20; // for example
private String _theString;
public String CappedString {
get { return _theString; }
set {
_theString = value != null && value.Length > MaxLength
? value.Substring(0, MaxLength)
: value;
}
}
}
You can apply this in whatever class needs to implement it. Just carry over the private field, the constant, and the property CappedString.
GUI.TextField lets you pass a max length in. You have two to choose from:
static function TextField (position : Rect, text : String, maxLength : int) : String
static function TextField (position : Rect, text : String, maxLength : int, style : GUIStyle) : String
Related
Not a duplicate of this.
I want to make a string have a max length. It should never pass this length. Lets say a 20 char length. If the provided string is > 20, take the first 20 string and discard the rest.
The answers on that question shows how to cap a string with a function but I want to do it directly without a function. I want the string length check to happen each time the string is written to.
Below is what I don't want to do:
string myString = "my long string";
myString = capString(myString, 20); //<-- Don't want to call a function each time
string capString(string strToCap, int strLen)
{
...
}
I was able to accomplish this with a property:
const int Max_Length = 20;
private string _userName;
public string userName
{
get { return _userName; }
set
{
_userName = string.IsNullOrEmpty(value) ? "" : value.Substring(0, Max_Length);
}
}
Then I can easily use it whout calling a function to cap it:
userName = "Programmer";
The problem with this is that every string I want to cap must have multiple variables defined for them. In this case, the _userName and the userName (property) variables.
Any clever way of doing this without creating multiple variables for each string and at the-same time, not having to call a function each time I want to modify the string?
Interesting situation - I would suggest creating a struct and then defining an implicit conversion operator for it, similar to what was done in this Stack Overflow question.
public struct CappedString
{
int Max_Length;
string val;
public CappedString(string str, int maxLength = 20)
{
Max_Length = maxLength;
val = (string.IsNullOrEmpty(str)) ? "" :
(str.Length <= Max_Length) ? str : str.Substring(0, Max_Length);
}
// From string to CappedString
public static implicit operator CappedString(string str)
{
return new CappedString(str);
}
// From CappedString to string
public static implicit operator string(CappedString str)
{
return str.val;
}
// To making using Debug.Log() more convenient
public override string ToString()
{
return val;
}
// Then overload the rest of your operators for other common string operations
}
Later you can use it like so:
// Implicitly convert string to CappedString
CappedString cappedString = "newString";
// Implicitly convert CappedString to string
string normalString = cappedString;
// Initialize with non-default max length
CappedString cappedString30 = new CappedString("newString", 30);
Note: This isn't perfect solution, unfortunately - because the implicit conversion doesn't give a way to transfer existing values to the new instance, any CappedString initialized with a non-default length value will need to be assigned to using the constructor, or its length limit will revert back to its default.
Create a class with a string property, and put all of that code there. Then, you can use s.Value anywhere as a string with the needed characteristic.
Something like:
class Superstring
{
int max_Length = 20;
string theString;
public Superstring() { }
public Superstring(int maxLength) { max_Length = maxLength; }
public Superstring(string initialValue) { Value = initialValue; }
public Superstring(int maxLength, string initialValue) { max_Length = maxLength; Value = initialValue; }
public string Value { get { return theString; } set { theString = string.IsNullOrEmpty(value) ? value : value.Substring(0, Math.Min(max_Length, value.Length)); } }
}
and use:
Superstring s = new Superstring("z");
s.Value = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
string s2 = s.Value;
I have a string with the following structure:
Student Name________AgeAddress_______________________Bithday___Lvl
Example:
Jonh Smith 016Some place in NY, USA 01/01/2014L01
As you can see, there is no delimited character like | or ,
Also, there is no space between fields (if you check, there is no space between Age/Address and Birthday/Level.
The size of each field is static so if data's length is less then it will contains white spaces.
I have a class that need to be filled with that information:
public class StudentData
{
public char[] _name = new char[20];
public string name;
public char[] _age = new char[3];
public string age;
public char[] _address = new char[30];
public string address;
public char[] _bday = new char[10];
public string bday;
public char[] _level = new char[3];
public string level;
}
Is there any way to do this automatically and dynamically?
I mean I really don't want to code like this:
myClass.name = stringLine.substring(0,19);
myClass.age = stringLine.substring(20,22);
That's because I have way more fields that the ones added in this example & way more string lines with other different data.
Update: There were supposed to be a lot of spaces between "Smith" and "016", but I don't know how to edit it.
Update2: If I use StringReader.Read() I can evade to use substring and indexes, but it isn't still so dynamically because I would need to repeat those 3 lines for each field.
StringReader reader = new StringReader(stringLine);
reader.Read(myClass._name, 0 myClass._name.Length);
myClass.name = new string(myClass._name);
Given your requirement I came up with an interesting solution. All be-it it may be more complex and longer than using the String.SubString() method as stated.
However this solution is transferable to other types and other string. I used a concept of Attributes, Properties, and Reflection to parse a string by a Fixed Length and setting the class Properties.
Note I did change your StudentData class to follow a more conventional coding style. Following this handy guide on MSDN: http://msdn.microsoft.com/en-us/library/xzf533w0(v=vs.71).aspx
Here is the new StudentData class. Note it uses the properties as opposed to fields. (Not discussed here).
public class StudentData
{
string name;
string age;
string address;
string bday;
string level;
[FixedLengthDelimeter(0, 20)]
public string Name { get { return this.name; } set { this.name = value; } }
[FixedLengthDelimeter(1, 3)]
public string Age { get { return this.age; } set { this.age = value; } }
[FixedLengthDelimeter(2, 30)]
public string Address { get { return this.address; } set { this.address = value; } }
[FixedLengthDelimeter(3, 10)]
public string BDay { get { return this.bday; } set { this.bday = value; } }
[FixedLengthDelimeter(4, 3)]
public string Level { get { return this.level; } set { this.level = value; } }
}
Note on each of the properties there is an Attribute called FixedLengthDelimeter that takes two parameters.
OrderNumber
FixedLength
The OrderNumber parameter denotes the order in the string (not the position) but the order in which we process from the string. The second parameter denotes the Length of the string when parsing the string. Here is the full attribute class.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class FixedLengthDelimeterAttribute : Attribute
{
public FixedLengthDelimeterAttribute(int orderNumber, int fixedLength)
{
this.fixedLength = fixedLength;
this.orderNumber = orderNumber;
}
readonly int fixedLength;
readonly int orderNumber;
public int FixedLength { get { return this.fixedLength; } }
public int OrderNumber { get { return this.orderNumber; } }
}
Now the attribute is simple enough. Accepts the two paramters we discussed eariler in the constructor.
Finally there is another method to parse the string into the object type such as.
public static class FixedLengthFormatter
{
public static T ParseString<T>(string inputString)
{
Type tType = typeof(T);
var properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public); //;.Where(x => x.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false).Count() > 0);
T newT = (T)Activator.CreateInstance(tType);
Dictionary<PropertyInfo, FixedLengthDelimeterAttribute> dictionary = new Dictionary<PropertyInfo, FixedLengthDelimeterAttribute>();
foreach (var property in properties)
{
var atts = property.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false);
if (atts.Length == 0)
continue;
dictionary[property] = atts[0] as FixedLengthDelimeterAttribute;
}
foreach (var kvp in dictionary.OrderBy(x => x.Value.OrderNumber))
{
int length = kvp.Value.FixedLength;
if (inputString.Length < length)
throw new Exception("error on attribute order number:" + kvp.Value.OrderNumber + " the string is too short.");
string piece = inputString.Substring(0, length);
inputString = inputString.Substring(length);
kvp.Key.SetValue(newT, piece.Trim(), null);
}
return newT;
}
}
The method above is what does the string parsing. It is a pretty basic utility that reads all the properties that have the FixedLengthDelimeter attribute applied a Dictionary. That dictionary is then enumerated (ordered by OrderNumber) and then calling the SubString() method twice on the input string.
The first substring is to parse the next Token while the second substring resets the inputString to start processing the next token.
Finally as it is parsing the string it is then applying the parsed string to the property of the class Type provided to the method.
Now this can be used simply like this:
string data1 = "Jonh Smith 016Some place in NY, USA 01/01/2014L01";
StudentData student = FixedLengthFormatter.ParseString<StudentData>(data1);
What this does:
Parses a string against property attributes in a fixed length format.
What this does not do:
It does convert the parsed strings to another type. Therefore all the properties must be a string. (this can be easily adapted by adding some type casting logic in).
It is not well tested. This is only tested against a few samples.
It is not by all means the only or best solution out there.
You could use FileHelpers library (NuGet).
Just define the structure of your input file with attributes:
[FixedLengthRecord]
public class StudentData
{
[FieldFixedLength(20)]
[FieldTrim(TrimMode.Right)]
public string name;
[FieldFixedLength(3)]
public string age;
[FieldFixedLength(30)]
[FieldTrim(TrimMode.Right)]
public string address;
[FieldFixedLength(10)]
public string bday;
[FieldFixedLength(3)]
public string level;
}
Then simply read the file using FileHelperEngine<T>:
var engine = new FileHelperEngine<StudentData>();
var students = engine.ReadFile(filename);
I have an enum like this:
public enum Global
{
txt_test = 123
}
Now I want to use a call like this:
var text = lib.Get(Global.txt_test);
Method:
public TextString Get(Enum enumeration)
{
string name = enumeration.ToString();
int index = ?; // (int)enumeration not working
...
}
How to get the index of an enum in this case?
Or am I doing it wrong at all?
Thank you.
Solution:
public TextString Get(Enum enumeration)
{
string name = enumeration.ToString();
int index = Convert.ToInt32(enumeration);
...
}
Enum are convertible to int for retrieving their values:
public TextString Get(Enum enumeration)
{
string name = enumeration.ToString();
int index = Convert.ToInt32(enumeration);
// ...
return null;
}
Note that this will work because your enumeration is type of int by default. Enums can still be other value type like long :
enum Range : long { Max = 2147483648L, Min = 255L };
In this case, the conversion will lost precision.
If you only need the enum value (what you are calling "index") as a string, the best way is to use custom format strings as documented here: http://msdn.microsoft.com/en-us/library/c3s1ez6e%28v=vs.110%29.aspx
For example:
public TextString Get(Enum enumeration)
{
string index = enumeration.ToString("D");
// ...
return null;
}
I have the following class:
public class VendorClass {
public int VendorID { get; set; }
public string VendorName { get; set; }
}
The fields above match fields in the database table.
In the case of say VendorName, how do I give it a field width ?
VendorName maps to a field in the database which is varchar(15)
You can't limit the length of the string but you can use properties with backing fields to achieve the desired result :
public class VendorClass
{
public int VendorID { get; set; }
private string _vendorName;
public string VendorName
{
get { return _vendorName; }
set
{
if (value.Length > 15)
{
_vendorName = value.Substring(0,15);
} else {
_vendorName = value;
}
}
}
}
Strings in C# have almost-arbitrary length.
When loading from your database, it will automatically accommodate the actual string length. When saving to the database, your business logic, data layer or ORM (as appropriate) will need to ensure the proper maximum length.
A string can't have a set length in C#. You will have to handle the db length through some other mechanism like validation. Can't really tell you more without more details.
I would question why you would do this in c# code. However this link has a couple of ways around this. I suppose either truncation or taking a subsring is the best option. You could also make sure that the UI (or the model-view) takes care of details such as this.
I am not sure exactly what you are asking, but if you want to know the maximum length of a string, this question can help you.
If you want to limit the number of characters entered, I would suggest that you use server-side validation and/or client-side validation.
I just met a problem like what you described and found a way to create a limited length's string. Maybe a little inflexible but concise when there are only finite varchar length definitions in database.
Firstly introduce some basic classes:
public class Length16
{
public static int StringLength { get => 16; }
}
public class Length8
{
public static int StringLength { get => 8; }
}
public class Length15
{
public static int StringLength { get => 15; }
}
public class LimitedLengthString<T>
{
private string _sValue;
public LimitedLengthString(string sNewValue)
{
_sValue = sNewValue;
}
public static implicit operator LimitedLengthString<T>(string sNewValue)
{
var prop = typeof(T).GetProperty("StringLength");
int iLength = (int)prop.GetValue(null);
if (sNewValue.Length > iLength)
{
throw new Exception($"New string is too long! Allowed length {iLength}.");
}
return new LimitedLengthString<T>(sNewValue);
}
public static implicit operator string(LimitedLengthString<T> sSource)
{
return sSource.ToString();
}
public override string ToString()
{
return _sValue;
}
}
public class AutoTruncatedString<T>
{
private string _sValue;
public AutoTruncatedString(string sNewValue)
{
_sValue = sNewValue;
}
public static implicit operator AutoTruncatedString<T>(string sNewValue)
{
var prop = typeof(T).GetProperty("StringLength");
int iLength = (int)prop.GetValue(null);
return new AutoTruncatedString<T>(sNewValue.Substring(0, iLength));
}
public static implicit operator string(AutoTruncatedString<T> sSource)
{
return sSource.ToString();
}
public override string ToString()
{
return _sValue;
}
}
Use them like this:
LimitedLengthString<Length8> sLimitedLength8;
sLimitedLength8 = "asdfgasdfg"; // will error out
AutoTruncatedString<Length8> sAutoTruncated8;
sAutoTruncated8 = "asdfgasdfg"; // will be truncated
sLimitedLength8 will throw an error if you try to assign a string longer than 8 and sAutoTruncated8 will truncate the string you assign to it.
For you, you can define the VendorName this way:
public LimitedLengthString<Length15> VendorName { get; set; }
Hope this could help you.
I have a string property that I would like to be able to force two things with:
- It can only be set to specific vaues within a pre-defined list,
- Error checking of the property's value can be performed at compile time.
An enum fits the bill perfectly except that in my list of pre-defined strings there is one with a hyphen and enum values cannot contain hyphens. To illustrate the ideal solution if an enum could contain hyphens I would create an enum of:
public enum SIPEventPackagesEnum
{
dialog,
message-summary,
refer
}
To use:
SIPEventPackagesEnum EventPackage = SIPEventPackagesEnum.message-summary;
To set:
string eventPackageStr = "message-summary";
SIPEventPackagesEnum EventPackage = (SIPEventPackagesEnum)Enum.Parse(typeof(SIPEventPackagesEnum), eventPackageStr, true);
In the above cases it's impossible to set the EventPackage property to anything but one of the enum values and is intuitive to use since intellisense will list the available options.
Due to the inability to use a hyphen, and I cannot change the pre-defined list to remove the hyphen, the very crude approach is to use a struct and have a "Value" property on the struct that does the enforcing in its setter, see below. It's very verbose compared to using an enum and also doesn't allow any compile time checking and isn't very intuitive.
Has anyone encountered this problem before and have a better solution? I have multiple lists with items containing hyphens so it's not a once off.
public struct SIPEventPackages
{
public const string DIALOG = "dialog";
public const string MESSAGE_SUMMARY = "message-summary";
public const string REFER = "refer";
public string Value
{
get { return Value; }
set
{
if (IsValid(value))
{
Value = value.ToLower();
}
else
{
throw new ArgumentException(value + " is invalid for a SIP event package.");
}
}
}
public bool IsValid(string value)
{
if (value.IsNullOrBlank())
{
return false;
}
else if (value.ToLower() == DIALOG || value.ToLower() == MESSAGE_SUMMARY || value.ToLower() == REFER)
{
return true;
}
else
{
return false;
}
}
public override string ToString()
{
return Value;
}
}
Seems this is simple using an delegate
Func<string, bool> isValid = str =>
{
List<string> validLst = new List<string>() { "dialog","message-summary","refer" };
if (validLst.Find(x => string.Equals(x,str,StringComparison.InvariantCultureIgnoreCase)) == null)
return false;
return true;
};
var teststr1 = "message-summary";
var teststr2 = "wrongone";
isValid(teststr1);
isValid(teststr2);
Uptdate:
Otherwise you can use the enum approach in a little different way. Have a enum value without an hyphen. And just strip the hyphens from your source string when parse the enum values. this will work as you expected
public enum SIPEventPackagesEnum
{
dialog,
messagesummary,
refer
}
string eventPackageStr = "message-summary";
SIPEventPackagesEnum EventPackage = (SIPEventPackagesEnum)Enum.Parse(typeof(SIPEventPackagesEnum), eventPackageStr.Replace("-",""), true);
You could create the enum without -, then have a static Dictionary<SIPEventPackagesEnum
,string> in a helper class mapping from enum value to string to convert from enum to string, then use Enum.Parse(typeof(SIPEventPackagesEnum), str.Replace("-", "")) when converting from string to enum.
Or use _ instead of - and replace _ with - and vice versa when required
Or use camel case in the enum values and replace a capital letter within the enum name with "-<lowercase letter>" using a regex
I managed to fine tune my approach so that it's almost as good as an enum albeit with a lot more plumbing code required. It's worth the plumbing code to save potential misues problems in the future.
public struct SIPEventPackage
{
public static SIPEventPackage None = new SIPEventPackage(null);
public static SIPEventPackage Dialog = new SIPEventPackage("dialog");
public static SIPEventPackage MessageSummary = new SIPEventPackage("message-summary");
public static SIPEventPackage Refer = new SIPEventPackage("refer");
private string m_value;
private SIPEventPackage(string value)
{
m_value = value;
}
public override string ToString()
{
return m_value;
}
public static SIPEventPackage Parse(string value)
{
if (!IsValid(value))
{
throw new ArgumentException("The value is not valid for a SIPEventPackage.");
}
else
{
string trimmedValue = value.Trim().ToLower();
switch (trimmedValue)
{
case "dialog": return SIPEventPackage.Dialog;
case "message-summary": return SIPEventPackage.MessageSummary;
case "refer": return SIPEventPackage.Refer;
default: throw new ArgumentException("The value is not valid for a SIPEventPackage.");
}
}
}
}
There's a little bit more plumbing required, implementing an IsValid method and operator == and a few more, but the main thing is I can now use the struct in almost an identical way to an enum and can have items with hyphens.